2 * This file is part of the theme implementation for form controls in WebCore.
4 * Copyright (C) 2005, 2006 Apple Computer, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
23 #import "RenderThemeMac.h"
25 #import "CSSValueKeywords.h"
27 #import "FoundationExtras.h"
29 #import "GraphicsContext.h"
31 #import "RenderView.h"
32 #import "WebCoreSystemInterface.h"
33 #import "cssstyleselector.h"
34 #import "RenderPopupMenuMac.h"
36 // The methods in this file are specific to the Mac OS X platform.
56 static RenderThemeMac macTheme;
60 RenderThemeMac::RenderThemeMac()
64 , resizeCornerImage(0)
68 Color RenderThemeMac::platformActiveSelectionBackgroundColor() const
70 NSColor* color = [[NSColor selectedTextBackgroundColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
71 return Color((int)(255 * [color redComponent]), (int)(255 * [color greenComponent]), (int)(255 * [color blueComponent]));
74 Color RenderThemeMac::platformInactiveSelectionBackgroundColor() const
76 NSColor* color = [[NSColor secondarySelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
77 return Color((int)(255 * [color redComponent]), (int)(255 * [color greenComponent]), (int)(255 * [color blueComponent]));
80 void RenderThemeMac::systemFont(int propId, FontDescription& fontDescription) const
82 static FontDescription systemFont;
83 static FontDescription smallSystemFont;
84 static FontDescription menuFont;
85 static FontDescription labelFont;
86 static FontDescription miniControlFont;
87 static FontDescription smallControlFont;
88 static FontDescription controlFont;
90 FontDescription* cachedDesc;
93 case CSS_VAL_SMALL_CAPTION:
94 cachedDesc = &smallSystemFont;
95 if (!smallSystemFont.isAbsoluteSize())
96 font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
99 cachedDesc = &menuFont;
100 if (!menuFont.isAbsoluteSize())
101 font = [NSFont menuFontOfSize:[NSFont systemFontSize]];
103 case CSS_VAL_STATUS_BAR:
104 cachedDesc = &labelFont;
105 if (!labelFont.isAbsoluteSize())
106 font = [NSFont labelFontOfSize:[NSFont labelFontSize]];
108 case CSS_VAL__WEBKIT_MINI_CONTROL:
109 cachedDesc = &miniControlFont;
110 if (!miniControlFont.isAbsoluteSize())
111 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]];
113 case CSS_VAL__WEBKIT_SMALL_CONTROL:
114 cachedDesc = &smallControlFont;
115 if (!smallControlFont.isAbsoluteSize())
116 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
118 case CSS_VAL__WEBKIT_CONTROL:
119 cachedDesc = &controlFont;
120 if (!controlFont.isAbsoluteSize())
121 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
124 cachedDesc = &systemFont;
125 if (!systemFont.isAbsoluteSize())
126 font = [NSFont systemFontOfSize:[NSFont systemFontSize]];
130 cachedDesc->setIsAbsoluteSize(true);
131 cachedDesc->setGenericFamily(FontDescription::SerifFamily);
132 cachedDesc->firstFamily().setFamily([font familyName]);
133 cachedDesc->setComputedSize([font pointSize]);
134 cachedDesc->setSpecifiedSize([font pointSize]);
135 NSFontTraitMask traits = [[NSFontManager sharedFontManager] traitsOfFont:font];
136 cachedDesc->setBold(traits & NSBoldFontMask);
137 cachedDesc->setItalic(traits & NSItalicFontMask);
139 fontDescription = *cachedDesc;
142 bool RenderThemeMac::isControlStyled(const RenderStyle* style, const BorderData& border,
143 const BackgroundLayer& background, const Color& backgroundColor) const
145 if (style->appearance() == TextFieldAppearance || style->appearance() == TextAreaAppearance)
146 return style->border() != border;
147 return RenderTheme::isControlStyled(style, border, background, backgroundColor);
150 void RenderThemeMac::paintResizeControl(GraphicsContext* c, const IntRect& r)
152 if (!resizeCornerImage)
153 resizeCornerImage = Image::loadPlatformResource("textAreaResizeCorner");
155 IntPoint imagePoint(r.right() - resizeCornerImage->width(), r.bottom() - resizeCornerImage->height());
156 c->drawImage(resizeCornerImage, imagePoint);
159 void RenderThemeMac::adjustRepaintRect(const RenderObject* o, IntRect& r)
161 switch (o->style()->appearance()) {
162 case CheckboxAppearance: {
163 // Since we query the prototype cell, we need to update its state to match.
164 setCheckboxCellState(o, r);
166 // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
167 // shadow" and the check. We don't consider this part of the bounds of the control in WebKit.
168 r = inflateRect(r, checkboxSizes()[[checkbox controlSize]], checkboxMargins());
171 case RadioAppearance: {
172 // Since we query the prototype cell, we need to update its state to match.
173 setRadioCellState(o, r);
175 // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
176 // shadow" and the check. We don't consider this part of the bounds of the control in WebKit.
177 r = inflateRect(r, radioSizes()[[radio controlSize]], radioMargins());
180 case PushButtonAppearance:
181 case ButtonAppearance: {
182 // Since we query the prototype cell, we need to update its state to match.
183 setButtonCellState(o, r);
185 // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
186 // shadow" and the check. We don't consider this part of the bounds of the control in WebKit.
187 if ([button bezelStyle] == NSRoundedBezelStyle)
188 r = inflateRect(r, buttonSizes()[[button controlSize]], buttonMargins());
191 case MenulistAppearance: {
192 setPopupButtonCellState(o, r);
193 r = inflateRect(r, popupButtonSizes()[[popupButton controlSize]], popupButtonMargins());
201 IntRect RenderThemeMac::inflateRect(const IntRect& r, const IntSize& size, const int* margins) const
203 // Only do the inflation if the available width/height are too small. Otherwise try to
204 // fit the glow/check space into the available box's width/height.
205 int widthDelta = r.width() - (size.width() + margins[leftMargin] + margins[rightMargin]);
206 int heightDelta = r.height() - (size.height() + margins[topMargin] + margins[bottomMargin]);
208 if (widthDelta < 0) {
209 result.setX(result.x() - margins[leftMargin]);
210 result.setWidth(result.width() - widthDelta);
212 if (heightDelta < 0) {
213 result.setY(result.y() - margins[topMargin]);
214 result.setHeight(result.height() - heightDelta);
219 void RenderThemeMac::updateCheckedState(NSCell* cell, const RenderObject* o)
221 bool oldIndeterminate = [cell state] == NSMixedState;
222 bool indeterminate = isIndeterminate(o);
223 bool checked = isChecked(o);
225 if (oldIndeterminate != indeterminate) {
226 [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)];
230 bool oldChecked = [cell state] == NSOnState;
231 if (checked != oldChecked)
232 [cell setState:checked ? NSOnState : NSOffState];
235 void RenderThemeMac::updateEnabledState(NSCell* cell, const RenderObject* o)
237 bool oldEnabled = [cell isEnabled];
238 bool enabled = isEnabled(o);
239 if (enabled != oldEnabled)
240 [cell setEnabled:enabled];
243 void RenderThemeMac::updateFocusedState(NSCell* cell, const RenderObject* o)
245 // FIXME: Need to add a key window test here, or the element will look
246 // focused even when in the background.
247 bool oldFocused = [cell showsFirstResponder];
248 bool focused = (o->element() && o->document()->focusNode() == o->element()) && (o->style()->outlineStyleIsAuto());
249 if (focused != oldFocused)
250 [cell setShowsFirstResponder:focused];
253 void RenderThemeMac::updatePressedState(NSCell* cell, const RenderObject* o)
255 bool oldPressed = [cell isHighlighted];
256 bool pressed = (o->element() && o->element()->active());
257 if (pressed != oldPressed)
258 [cell setHighlighted:pressed];
261 short RenderThemeMac::baselinePosition(const RenderObject* o) const
263 if (o->style()->appearance() == CheckboxAppearance || o->style()->appearance() == RadioAppearance)
264 return o->marginTop() + o->height() - 2; // The baseline is 2px up from the bottom of the checkbox/radio in AppKit.
265 return RenderTheme::baselinePosition(o);
268 bool RenderThemeMac::controlSupportsTints(const RenderObject* o) const
273 // Checkboxes only have tint when checked.
274 if (o->style()->appearance() == CheckboxAppearance)
277 // For now assume other controls have tint if enabled.
281 NSControlSize RenderThemeMac::controlSizeForFont(RenderStyle* style) const
283 int fontSize = style->fontSize();
285 return NSRegularControlSize;
287 return NSSmallControlSize;
288 return NSMiniControlSize;
291 void RenderThemeMac::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize)
294 if (minSize.width() >= sizes[NSRegularControlSize].width() &&
295 minSize.height() >= sizes[NSRegularControlSize].height())
296 size = NSRegularControlSize;
297 else if (minSize.width() >= sizes[NSSmallControlSize].width() &&
298 minSize.height() >= sizes[NSSmallControlSize].height())
299 size = NSSmallControlSize;
301 size = NSMiniControlSize;
302 if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same.
303 [cell setControlSize:size];
306 IntSize RenderThemeMac::sizeForFont(RenderStyle* style, const IntSize* sizes) const
308 return sizes[controlSizeForFont(style)];
311 void RenderThemeMac::setSizeFromFont(RenderStyle* style, const IntSize* sizes) const
313 // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
314 IntSize size = sizeForFont(style, sizes);
315 if (style->width().isIntrinsicOrAuto() && size.width() > 0)
316 style->setWidth(Length(size.width(), Fixed));
317 if (style->height().isAuto() && size.height() > 0)
318 style->setHeight(Length(size.height(), Fixed));
321 void RenderThemeMac::setFontFromControlSize(CSSStyleSelector* selector, RenderStyle* style, NSControlSize controlSize) const
323 FontDescription fontDescription;
324 fontDescription.setIsAbsoluteSize(true);
325 fontDescription.setGenericFamily(FontDescription::SerifFamily);
327 NSFont* font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSize]];
328 fontDescription.firstFamily().setFamily([font familyName]);
329 fontDescription.setComputedSize([font pointSize]);
330 fontDescription.setSpecifiedSize([font pointSize]);
332 if (style->setFontDescription(fontDescription))
333 style->font().update();
336 void RenderThemeMac::addIntrinsicMargins(RenderStyle* style, NSControlSize size) const
338 // Cut out the intrinsic margins completely if we end up using mini controls.
339 if (size == NSMiniControlSize)
342 // Intrinsic margin value.
345 // FIXME: Using width/height alone and not also dealing with min-width/max-width is flawed.
346 if (style->width().isIntrinsicOrAuto()) {
347 if (style->marginLeft().quirk())
348 style->setMarginLeft(Length(m, Fixed));
349 if (style->marginRight().quirk())
350 style->setMarginRight(Length(m, Fixed));
353 if (style->height().isAuto()) {
354 if (style->marginTop().quirk())
355 style->setMarginTop(Length(m, Fixed));
356 if (style->marginBottom().quirk())
357 style->setMarginBottom(Length(m, Fixed));
361 bool RenderThemeMac::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
363 // Determine the width and height needed for the control and prepare the cell for painting.
364 setCheckboxCellState(o, r);
366 // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
367 // shadow" and the check. We don't consider this part of the bounds of the control in WebKit.
368 IntRect inflatedRect = inflateRect(r, checkboxSizes()[[checkbox controlSize]], checkboxMargins());
369 [checkbox drawWithFrame:NSRect(inflatedRect) inView:o->view()->frameView()->getDocumentView()];
370 [checkbox setControlView: nil];
375 const IntSize* RenderThemeMac::checkboxSizes() const
377 static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) };
381 const int* RenderThemeMac::checkboxMargins() const
383 static const int margins[3][4] =
389 return margins[[checkbox controlSize]];
392 void RenderThemeMac::setCheckboxCellState(const RenderObject* o, const IntRect& r)
395 checkbox = HardRetainWithNSRelease([[NSButtonCell alloc] init]);
396 [checkbox setButtonType:NSSwitchButton];
397 [checkbox setTitle:nil];
398 [checkbox setAllowsMixedState:YES];
401 // Set the control size based off the rectangle we're painting into.
402 setControlSize(checkbox, checkboxSizes(), r.size());
404 // Update the various states we respond to.
405 updateCheckedState(checkbox, o);
406 updateEnabledState(checkbox, o);
407 updatePressedState(checkbox, o);
408 updateFocusedState(checkbox, o);
411 void RenderThemeMac::setCheckboxSize(RenderStyle* style) const
413 // If the width and height are both specified, then we have nothing to do.
414 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
417 // Use the font size to determine the intrinsic width of the control.
418 setSizeFromFont(style, checkboxSizes());
421 bool RenderThemeMac::paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
423 // Determine the width and height needed for the control and prepare the cell for painting.
424 setRadioCellState(o, r);
426 // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
427 // shadow" and the check. We don't consider this part of the bounds of the control in WebKit.
428 IntRect inflatedRect = inflateRect(r, radioSizes()[[radio controlSize]], radioMargins());
429 [radio drawWithFrame:NSRect(inflatedRect) inView:o->view()->frameView()->getDocumentView()];
430 [radio setControlView: nil];
435 const IntSize* RenderThemeMac::radioSizes() const
437 static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) };
441 const int* RenderThemeMac::radioMargins() const
443 static const int margins[3][4] =
449 return margins[[radio controlSize]];
452 void RenderThemeMac::setRadioCellState(const RenderObject* o, const IntRect& r)
455 radio = HardRetainWithNSRelease([[NSButtonCell alloc] init]);
456 [radio setButtonType:NSRadioButton];
457 [radio setTitle:nil];
460 // Set the control size based off the rectangle we're painting into.
461 setControlSize(radio, radioSizes(), r.size());
463 // Update the various states we respond to.
464 updateCheckedState(radio, o);
465 updateEnabledState(radio, o);
466 updatePressedState(radio, o);
467 updateFocusedState(radio, o);
471 void RenderThemeMac::setRadioSize(RenderStyle* style) const
473 // If the width and height are both specified, then we have nothing to do.
474 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
477 // Use the font size to determine the intrinsic width of the control.
478 setSizeFromFont(style, radioSizes());
481 void RenderThemeMac::setButtonPaddingFromControlSize(RenderStyle* style, NSControlSize size) const
483 // Just use 8px. AppKit wants to use 11px for mini buttons, but that padding is just too large
484 // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is
485 // by definition constrained, since we select mini only for small cramped environments.
486 // This also guarantees the HTML4 <button> will match our rendering by default, since we're using a consistent
488 const int padding = 8;
489 style->setPaddingLeft(Length(padding, Fixed));
490 style->setPaddingRight(Length(padding, Fixed));
491 style->setPaddingTop(Length(0, Fixed));
492 style->setPaddingBottom(Length(0, Fixed));
495 void RenderThemeMac::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
497 // There are three appearance constants for buttons.
498 // (1) Push-button is the constant for the default Aqua system button. Push buttons will not scale vertically and will not allow
499 // custom fonts or colors. <input>s use this constant. This button will allow custom colors and font weights/variants but won't
501 // (2) square-button is the constant for the square button. This button will allow custom fonts and colors and will scale vertically.
502 // (3) Button is the constant that means "pick the best button as appropriate." <button>s use this constant. This button will
503 // also scale vertically and allow custom fonts and colors. It will attempt to use Aqua if possible and will make this determination
504 // solely on the rectangle of the control.
506 // Determine our control size based off our font.
507 NSControlSize controlSize = controlSizeForFont(style);
509 // Add in intrinsic margins
510 addIntrinsicMargins(style, controlSize);
512 if (style->appearance() == PushButtonAppearance) {
514 style->resetBorder();
516 // Height is locked to auto.
517 style->setHeight(Length(Auto));
519 // White-space is locked to pre
520 style->setWhiteSpace(PRE);
522 // Set the button's vertical size.
523 setButtonSize(style);
525 // Add in the padding that we'd like to use.
526 setButtonPaddingFromControlSize(style, controlSize);
528 // Our font is locked to the appropriate system font size for the control. To clarify, we first use the CSS-specified font to figure out
529 // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
530 // system font for the control size instead.
531 setFontFromControlSize(selector, style, controlSize);
533 // Set a min-height so that we can't get smaller than the mini button.
534 style->setMinHeight(Length(15, Fixed));
536 // Reset the top and bottom borders.
537 style->resetBorderTop();
538 style->resetBorderBottom();
542 const IntSize* RenderThemeMac::buttonSizes() const
544 static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
548 const int* RenderThemeMac::buttonMargins() const
550 static const int margins[3][4] =
556 return margins[[button controlSize]];
559 void RenderThemeMac::setButtonSize(RenderStyle* style) const
561 // If the width and height are both specified, then we have nothing to do.
562 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
565 // Use the font size to determine the intrinsic width of the control.
566 setSizeFromFont(style, buttonSizes());
569 void RenderThemeMac::setButtonCellState(const RenderObject* o, const IntRect& r)
572 button = HardRetainWithNSRelease([[NSButtonCell alloc] init]);
573 [button setTitle:nil];
574 [button setButtonType:NSMomentaryPushInButton];
577 // Set the control size based off the rectangle we're painting into.
578 if (o->style()->appearance() == SquareButtonAppearance ||
579 r.height() > buttonSizes()[NSRegularControlSize].height()) {
580 // Use the square button
581 if ([button bezelStyle] != NSShadowlessSquareBezelStyle)
582 [button setBezelStyle:NSShadowlessSquareBezelStyle];
583 } else if ([button bezelStyle] != NSRoundedBezelStyle)
584 [button setBezelStyle:NSRoundedBezelStyle];
586 setControlSize(button, buttonSizes(), r.size());
588 // Update the various states we respond to.
589 updateCheckedState(button, o);
590 updateEnabledState(button, o);
591 updatePressedState(button, o);
592 updateFocusedState(button, o);
595 bool RenderThemeMac::paintButton(RenderObject* o, const RenderObject::PaintInfo&, const IntRect& r)
597 // FIXME: Ignores the GraphicsContext in the PaintInfo and always draws into the current
598 // NSGraphicsContext instead.
600 // Determine the width and height needed for the control and prepare the cell for painting.
601 setButtonCellState(o, r);
603 // We inflate the rect as needed to account for padding included in the cell to accommodate the button
604 // shadow. We don't consider this part of the bounds of the control in WebKit.
605 IntSize size = buttonSizes()[[button controlSize]];
606 size.setWidth(r.width());
607 IntRect inflatedRect = r;
608 if ([button bezelStyle] == NSRoundedBezelStyle) {
609 // Center the button within the available space.
610 if (inflatedRect.height() > size.height()) {
611 inflatedRect.setY(inflatedRect.y() + (inflatedRect.height() - size.height()) / 2);
612 inflatedRect.setHeight(size.height());
615 // Now inflate it to account for the shadow.
616 inflatedRect = inflateRect(inflatedRect, size, buttonMargins());
619 [button drawWithFrame:NSRect(inflatedRect) inView:o->view()->frameView()->getDocumentView()];
620 [button setControlView:nil];
625 bool RenderThemeMac::paintTextField(RenderObject* o, const RenderObject::PaintInfo&, const IntRect& r)
627 // FIXME: Ignores the GraphicsContext in the PaintInfo and always draws into the current
628 // NSGraphicsContext instead.
629 wkDrawBezeledTextFieldCell(r, isEnabled(o) && !isReadOnlyControl(o));
633 void RenderThemeMac::adjustTextFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
635 // Add in intrinsic margins if the font size isn't too small
636 if (style->fontSize() >= 11)
637 addIntrinsicMargins(style, NSRegularControlSize);
640 bool RenderThemeMac::paintTextArea(RenderObject* o, const RenderObject::PaintInfo&, const IntRect& r)
642 // FIXME: Ignores the GraphicsContext in the PaintInfo and always draws into the current
643 // NSGraphicsContext instead.
644 wkDrawBezeledTextArea(r, isEnabled(o) && !isReadOnlyControl(o));
648 void RenderThemeMac::adjustTextAreaStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
650 // Add in intrinsic margins if the font size isn't too small
651 if (style->fontSize() >= 11)
652 addIntrinsicMargins(style, NSRegularControlSize);
655 const int* RenderThemeMac::popupButtonMargins() const
657 static const int margins[3][4] =
663 return margins[[popupButton controlSize]];
666 const IntSize* RenderThemeMac::popupButtonSizes() const
668 static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
672 const int* RenderThemeMac::popupButtonPadding(NSControlSize size) const
674 static const int padding[3][4] =
680 return padding[size];
683 void RenderThemeMac::setPopupPaddingFromControlSize(RenderStyle* style, NSControlSize size) const
685 style->setPaddingLeft(Length(popupButtonPadding(size)[leftPadding], Fixed));
686 style->setPaddingRight(Length(popupButtonPadding(size)[rightPadding], Fixed));
687 style->setPaddingTop(Length(popupButtonPadding(size)[topPadding], Fixed));
688 style->setPaddingBottom(Length(popupButtonPadding(size)[bottomPadding], Fixed));
691 bool RenderThemeMac::paintMenuList(RenderObject* o, const RenderObject::PaintInfo&, const IntRect& r)
693 setPopupButtonCellState(o, r);
695 IntRect inflatedRect = r;
696 IntSize size = popupButtonSizes()[[popupButton controlSize]];
697 size.setWidth(r.width());
699 // Now inflate it to account for the shadow.
700 inflatedRect = inflateRect(inflatedRect, size, popupButtonMargins());
702 [popupButton drawWithFrame:inflatedRect inView:o->view()->frameView()->getDocumentView()];
703 [popupButton setControlView:nil];
707 const float baseFontSize = 11.0;
708 const float baseArrowHeight = 4.0;
709 const float baseArrowWidth = 5.0;
710 const float baseSpaceBetweenArrows = 2.0;
711 const int arrowPaddingLeft = 6;
712 const int arrowPaddingRight = 6;
713 const int paddingBeforeSeparator = 4;
714 const int baseBorderRadius = 5;
715 const int styledPopupPaddingLeft = 8;
716 const int styledPopupPaddingTop = 1;
717 const int styledPopupPaddingBottom = 2;
719 static void TopGradientInterpolate( void *info, float const *inData, float *outData )
721 static float dark[4] = { 1, 1, 1, 0.4 };
722 static float light[4] = { 1, 1, 1, 0.15 };
725 for( i = 0; i < 4; i++ )
726 outData[i] = ( 1.0 - a ) * dark[i] + a * light[i];
729 static void BottomGradientInterpolate( void *info, float const *inData, float *outData )
731 static float dark[4] = { 1, 1, 1, 0 };
732 static float light[4] = { 1, 1, 1, 0.3 };
735 for( i = 0; i < 4; i++ )
736 outData[i] = ( 1.0 - a ) * dark[i] + a * light[i];
739 static void MainGradientInterpolate( void *info, float const *inData, float *outData )
741 static float dark[4] = { 0, 0, 0, 0.15 };
742 static float light[4] = { 0, 0, 0, 0 };
745 for( i = 0; i < 4; i++ )
746 outData[i] = ( 1.0 - a ) * dark[i] + a * light[i];
749 void RenderThemeMac::paintMenuListButtonGradients(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
751 CGContextRef context = i.p->platformContext();
755 int radius = o->style()->borderTopLeftRadius().width();
757 CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB();
759 FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0);
760 struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL };
761 CGFunctionRef topFunction = CGFunctionCreate( NULL, 1, NULL, 4, NULL, &topCallbacks );
762 CGShadingRef topShading = CGShadingCreateAxial( cspace, CGPointMake(topGradient.x(), topGradient.y()),
763 CGPointMake(topGradient.x(), topGradient.bottom()), topFunction, false, false );
765 FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0, r.width() - 2 * radius, r.height() / 2.0);
766 struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL };
767 CGFunctionRef bottomFunction = CGFunctionCreate( NULL, 1, NULL, 4, NULL, &bottomCallbacks );
768 CGShadingRef bottomShading = CGShadingCreateAxial( cspace, CGPointMake(bottomGradient.x(), bottomGradient.y()),
769 CGPointMake(bottomGradient.x(), bottomGradient.bottom()), bottomFunction, false, false );
771 struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL };
772 CGFunctionRef mainFunction = CGFunctionCreate( NULL, 1, NULL, 4, NULL, &mainCallbacks );
773 CGShadingRef mainShading = CGShadingCreateAxial( cspace, CGPointMake(r.x(), r.y()),
774 CGPointMake(r.x(), r.bottom()), mainFunction, false, false );
776 CGShadingRef leftShading = CGShadingCreateAxial( cspace, CGPointMake(r.x(), r.y()),
777 CGPointMake(r.x() + radius, r.y()), mainFunction, false, false );
779 CGShadingRef rightShading = CGShadingCreateAxial( cspace, CGPointMake(r.right(), r.y()),
780 CGPointMake(r.right() - radius, r.y()), mainFunction, false, false );
782 CGContextClipToRect(context, r);
783 i.p->addRoundedRectClip(r,
784 o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
785 o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
786 CGContextDrawShading(context, mainShading);
790 CGContextClipToRect(context, topGradient);
791 i.p->addRoundedRectClip(enclosingIntRect(topGradient),
792 o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
793 IntSize(), IntSize());
794 CGContextDrawShading(context, topShading);
798 CGContextClipToRect(context, bottomGradient);
799 i.p->addRoundedRectClip(enclosingIntRect(bottomGradient),
800 IntSize(), IntSize(),
801 o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
802 CGContextDrawShading(context, bottomShading);
806 CGContextClipToRect(context, r);
807 i.p->addRoundedRectClip(r,
808 o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
809 o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
810 CGContextDrawShading(context, leftShading);
811 CGContextDrawShading(context, rightShading);
817 bool RenderThemeMac::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
821 IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(),
822 r.y() + o->style()->borderTopWidth(),
823 r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(),
824 r.height() - o->style()->borderTopWidth() - o->style()->borderBottomWidth());
825 // Draw the gradients to give the styled popup menu a button appearance
826 paintMenuListButtonGradients(o, i, bounds);
828 float fontScale = o->style()->fontSize() / baseFontSize;
829 float centerY = bounds.y() + bounds.height() / 2.0;
830 float arrowHeight = baseArrowHeight * fontScale;
831 float arrowWidth = baseArrowWidth * fontScale;
832 float leftEdge = bounds.right() - arrowPaddingRight - arrowWidth;
833 float spaceBetweenArrows = baseSpaceBetweenArrows * fontScale;
835 i.p->setFillColor(o->style()->color());
836 i.p->setPen(Pen(o->style()->color()));
838 FloatPoint arrow1[3];
839 arrow1[0] = FloatPoint(leftEdge, centerY - spaceBetweenArrows / 2.0);
840 arrow1[1] = FloatPoint(leftEdge + arrowWidth, centerY - spaceBetweenArrows / 2.0);
841 arrow1[2] = FloatPoint(leftEdge + arrowWidth / 2.0, centerY - spaceBetweenArrows / 2.0 - arrowHeight);
843 // Draw the top arrow
844 i.p->drawConvexPolygon(3, arrow1, true);
846 FloatPoint arrow2[3];
847 arrow2[0] = FloatPoint(leftEdge, centerY + spaceBetweenArrows / 2.0);
848 arrow2[1] = FloatPoint(leftEdge + arrowWidth, centerY + spaceBetweenArrows / 2.0);
849 arrow2[2] = FloatPoint(leftEdge + arrowWidth / 2.0, centerY + spaceBetweenArrows / 2.0 + arrowHeight);
851 // Draw the bottom arrow
852 i.p->drawConvexPolygon(3, arrow2, true);
854 Color leftSeparatorColor(0, 0, 0, 40);
855 Color rightSeparatorColor(255, 255, 255, 40);
856 int separatorSpace = 2;
857 int leftEdgeOfSeparator = int(leftEdge - arrowPaddingLeft); // FIXME: Round?
859 // Draw the separator to the left of the arrows
860 i.p->setPen(Pen(leftSeparatorColor));
861 i.p->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()), IntPoint(leftEdgeOfSeparator, bounds.bottom()));
863 i.p->setPen(Pen(rightSeparatorColor));
864 i.p->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()), IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.bottom()));
870 void RenderThemeMac::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
872 NSControlSize controlSize = controlSizeForFont(style);
874 // Add in intrinsic margins
875 addIntrinsicMargins(style, controlSize);
877 style->resetBorder();
879 // Height is locked to auto.
880 style->setHeight(Length(Auto));
882 // White-space is locked to pre
883 style->setWhiteSpace(PRE);
885 // Set the foreground color to black when we have the aqua look
886 style->setColor(Color::black);
888 // Set the button's vertical size.
889 setButtonSize(style);
891 // Add in the padding that we'd like to use.
892 setPopupPaddingFromControlSize(style, controlSize);
894 // Our font is locked to the appropriate system font size for the control. To clarify, we first use the CSS-specified font to figure out
895 // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
896 // system font for the control size instead.
897 setFontFromControlSize(selector, style, controlSize);
900 void RenderThemeMac::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
902 // Add in intrinsic margins if the font size isn't too small
903 if (style->fontSize() >= 11)
904 addIntrinsicMargins(style, NSRegularControlSize);
906 float fontScale = style->fontSize() / baseFontSize;
907 float arrowWidth = baseArrowWidth * fontScale;
909 // We're overriding the padding to allow for the arrow control. WinIE doesn't honor padding on selects, so
910 // this shouldn't cause problems on the web. If IE7 changes that, we should reconsider this.
911 style->setPaddingLeft(Length(styledPopupPaddingLeft, Fixed));
912 style->setPaddingRight(Length(int(ceilf(arrowWidth + arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator)), Fixed));
913 style->setPaddingTop(Length(styledPopupPaddingTop, Fixed));
914 style->setPaddingBottom(Length(styledPopupPaddingBottom, Fixed));
916 if (style->hasBorderRadius())
917 style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
919 const int minHeight = 15;
920 style->setMinHeight(Length(minHeight, Fixed));
923 void RenderThemeMac::setPopupButtonCellState(const RenderObject* o, const IntRect& r)
926 popupButton = HardRetainWithNSRelease([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]);
927 [popupButton setUsesItemFromMenu:NO];
930 // Set the control size based off the rectangle we're painting into.
931 setControlSize(popupButton, popupButtonSizes(), r.size());
933 // Update the various states we respond to.
934 updateCheckedState(popupButton, o);
935 updateEnabledState(popupButton, o);
936 updatePressedState(popupButton, o);
937 updateFocusedState(popupButton, o);
940 RenderPopupMenu* RenderThemeMac::createPopupMenu(RenderArena* arena, Document* doc, RenderMenuList* menuList)
942 return new (arena) RenderPopupMenuMac(doc, menuList);
945 int RenderThemeMac::minimumMenuListSize(RenderStyle* style) const
947 int fontSize = style->fontSize();