RenderThemeWin.cpp revision 635860845790a19bf50bbc51ba8fb66a96dde068
1/* 2 * Copyright (C) 2006, 2007 Apple Inc. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 17 * Boston, MA 02111-1307, USA. 18 * 19 */ 20 21#include "config.h" 22#include "RenderThemeWin.h" 23 24#include "CSSStyleSheet.h" 25#include "CSSValueKeywords.h" 26#include "Document.h" 27#include "GraphicsContext.h" 28#include "HTMLElement.h" 29#include "HTMLSelectElement.h" 30#include "Icon.h" 31#include "RenderSlider.h" 32#include "SoftLinking.h" 33#include "UserAgentStyleSheets.h" 34 35#include <tchar.h> 36 37/* 38 * The following constants are used to determine how a widget is drawn using 39 * Windows' Theme API. For more information on theme parts and states see 40 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp 41 */ 42 43// Generic state constants 44#define TS_NORMAL 1 45#define TS_HOVER 2 46#define TS_ACTIVE 3 47#define TS_DISABLED 4 48#define TS_FOCUSED 5 49 50// Button constants 51#define BP_BUTTON 1 52#define BP_RADIO 2 53#define BP_CHECKBOX 3 54 55// Textfield constants 56#define TFP_TEXTFIELD 1 57#define TFS_READONLY 6 58 59// ComboBox constants (from tmschema.h) 60#define CP_DROPDOWNBUTTON 1 61 62// TrackBar (slider) parts 63#define TKP_TRACK 1 64#define TKP_TRACKVERT 2 65 66// TrackBar (slider) thumb parts 67#define TKP_THUMBBOTTOM 4 68#define TKP_THUMBTOP 5 69#define TKP_THUMBLEFT 7 70#define TKP_THUMBRIGHT 8 71 72// Trackbar (slider) thumb states 73#define TUS_NORMAL 1 74#define TUS_HOT 2 75#define TUS_PRESSED 3 76#define TUS_FOCUSED 4 77#define TUS_DISABLED 5 78 79// button states 80#define PBS_NORMAL 1 81#define PBS_HOT 2 82#define PBS_PRESSED 3 83#define PBS_DISABLED 4 84#define PBS_DEFAULTED 5 85 86SOFT_LINK_LIBRARY(uxtheme) 87SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (hwnd, pszClassList)) 88SOFT_LINK(uxtheme, CloseThemeData, HRESULT, WINAPI, (HANDLE hTheme), (hTheme)) 89SOFT_LINK(uxtheme, DrawThemeBackground, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT* pRect, const RECT* pClipRect), (hTheme, hdc, iPartId, iStateId, pRect, pClipRect)) 90SOFT_LINK(uxtheme, IsThemeActive, BOOL, WINAPI, (), ()) 91SOFT_LINK(uxtheme, IsThemeBackgroundPartiallyTransparent, BOOL, WINAPI, (HANDLE hTheme, int iPartId, int iStateId), (hTheme, iPartId, iStateId)) 92 93static bool haveTheme; 94 95using namespace std; 96 97namespace WebCore { 98 99// This is the fixed width IE and Firefox use for buttons on dropdown menus 100static const int dropDownButtonWidth = 17; 101 102static const int shell32MagnifierIconIndex = 22; 103 104// Default font size to match Firefox. 105static const float defaultControlFontPixelSize = 13; 106 107static const float defaultCancelButtonSize = 9; 108static const float minCancelButtonSize = 5; 109static const float maxCancelButtonSize = 21; 110static const float defaultSearchFieldResultsDecorationSize = 13; 111static const float minSearchFieldResultsDecorationSize = 9; 112static const float maxSearchFieldResultsDecorationSize = 30; 113static const float defaultSearchFieldResultsButtonWidth = 18; 114 115static bool gWebKitIsBeingUnloaded; 116 117void RenderThemeWin::setWebKitIsBeingUnloaded() 118{ 119 gWebKitIsBeingUnloaded = true; 120} 121 122#if !USE(SAFARI_THEME) 123RenderTheme* theme() 124{ 125 static RenderThemeWin winTheme; 126 return &winTheme; 127} 128#endif 129 130RenderThemeWin::RenderThemeWin() 131 : m_buttonTheme(0) 132 , m_textFieldTheme(0) 133 , m_menuListTheme(0) 134 , m_sliderTheme(0) 135{ 136 haveTheme = uxthemeLibrary() && IsThemeActive(); 137} 138 139RenderThemeWin::~RenderThemeWin() 140{ 141 // If WebKit is being unloaded, then uxtheme.dll is no longer available. 142 if (gWebKitIsBeingUnloaded || !uxthemeLibrary()) 143 return; 144 close(); 145} 146 147HANDLE RenderThemeWin::buttonTheme() const 148{ 149 if (haveTheme && !m_buttonTheme) 150 m_buttonTheme = OpenThemeData(0, L"Button"); 151 return m_buttonTheme; 152} 153 154HANDLE RenderThemeWin::textFieldTheme() const 155{ 156 if (haveTheme && !m_textFieldTheme) 157 m_textFieldTheme = OpenThemeData(0, L"Edit"); 158 return m_textFieldTheme; 159} 160 161HANDLE RenderThemeWin::menuListTheme() const 162{ 163 if (haveTheme && !m_menuListTheme) 164 m_menuListTheme = OpenThemeData(0, L"ComboBox"); 165 return m_menuListTheme; 166} 167 168HANDLE RenderThemeWin::sliderTheme() const 169{ 170 if (haveTheme && !m_sliderTheme) 171 m_sliderTheme = OpenThemeData(0, L"TrackBar"); 172 return m_sliderTheme; 173} 174 175void RenderThemeWin::close() 176{ 177 // This method will need to be called when the OS theme changes to flush our cached themes. 178 if (m_buttonTheme) 179 CloseThemeData(m_buttonTheme); 180 if (m_textFieldTheme) 181 CloseThemeData(m_textFieldTheme); 182 if (m_menuListTheme) 183 CloseThemeData(m_menuListTheme); 184 if (m_sliderTheme) 185 CloseThemeData(m_sliderTheme); 186 m_buttonTheme = m_textFieldTheme = m_menuListTheme = m_sliderTheme = 0; 187 188 haveTheme = uxthemeLibrary() && IsThemeActive(); 189} 190 191void RenderThemeWin::themeChanged() 192{ 193 close(); 194} 195 196String RenderThemeWin::extraDefaultStyleSheet() 197{ 198 return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet)); 199} 200 201String RenderThemeWin::extraQuirksStyleSheet() 202{ 203 return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet)); 204} 205 206bool RenderThemeWin::supportsHover(const RenderStyle*) const 207{ 208 // The Classic/2k look has no hover effects. 209 return haveTheme; 210} 211 212Color RenderThemeWin::platformActiveSelectionBackgroundColor() const 213{ 214 COLORREF color = GetSysColor(COLOR_HIGHLIGHT); 215 return Color(GetRValue(color), GetGValue(color), GetBValue(color)); 216} 217 218Color RenderThemeWin::platformInactiveSelectionBackgroundColor() const 219{ 220 // This color matches Firefox. 221 return Color(176, 176, 176); 222} 223 224Color RenderThemeWin::platformActiveSelectionForegroundColor() const 225{ 226 COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT); 227 return Color(GetRValue(color), GetGValue(color), GetBValue(color)); 228} 229 230Color RenderThemeWin::platformInactiveSelectionForegroundColor() const 231{ 232 return platformActiveSelectionForegroundColor(); 233} 234 235static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont, float fontSize) 236{ 237 fontDescription.setIsAbsoluteSize(true); 238 fontDescription.setGenericFamily(FontDescription::NoFamily); 239 fontDescription.firstFamily().setFamily(String(logFont.lfFaceName)); 240 fontDescription.setSpecifiedSize(fontSize); 241 fontDescription.setWeight(logFont.lfWeight >= 700 ? FontWeightBold : FontWeightNormal); // FIXME: Use real weight. 242 fontDescription.setItalic(logFont.lfItalic); 243} 244 245static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont) 246{ 247 fillFontDescription(fontDescription, logFont, abs(logFont.lfHeight)); 248} 249 250void RenderThemeWin::systemFont(int propId, FontDescription& fontDescription) const 251{ 252 static FontDescription captionFont; 253 static FontDescription controlFont; 254 static FontDescription smallCaptionFont; 255 static FontDescription menuFont; 256 static FontDescription iconFont; 257 static FontDescription messageBoxFont; 258 static FontDescription statusBarFont; 259 static FontDescription systemFont; 260 261 static bool initialized; 262 static NONCLIENTMETRICS ncm; 263 264 if (!initialized) { 265 initialized = true; 266 ncm.cbSize = sizeof(NONCLIENTMETRICS); 267 ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0); 268 } 269 270 switch (propId) { 271 case CSSValueIcon: { 272 if (!iconFont.isAbsoluteSize()) { 273 LOGFONT logFont; 274 ::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(logFont), &logFont, 0); 275 fillFontDescription(iconFont, logFont); 276 } 277 fontDescription = iconFont; 278 break; 279 } 280 case CSSValueMenu: 281 if (!menuFont.isAbsoluteSize()) 282 fillFontDescription(menuFont, ncm.lfMenuFont); 283 fontDescription = menuFont; 284 break; 285 case CSSValueMessageBox: 286 if (!messageBoxFont.isAbsoluteSize()) 287 fillFontDescription(messageBoxFont, ncm.lfMessageFont); 288 fontDescription = messageBoxFont; 289 break; 290 case CSSValueStatusBar: 291 if (!statusBarFont.isAbsoluteSize()) 292 fillFontDescription(statusBarFont, ncm.lfStatusFont); 293 fontDescription = statusBarFont; 294 break; 295 case CSSValueCaption: 296 if (!captionFont.isAbsoluteSize()) 297 fillFontDescription(captionFont, ncm.lfCaptionFont); 298 fontDescription = captionFont; 299 break; 300 case CSSValueSmallCaption: 301 if (!smallCaptionFont.isAbsoluteSize()) 302 fillFontDescription(smallCaptionFont, ncm.lfSmCaptionFont); 303 fontDescription = smallCaptionFont; 304 break; 305 case CSSValueWebkitSmallControl: 306 case CSSValueWebkitMiniControl: // Just map to small. 307 case CSSValueWebkitControl: // Just map to small. 308 if (!controlFont.isAbsoluteSize()) { 309 HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT); 310 if (hGDI) { 311 LOGFONT logFont; 312 if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0) 313 fillFontDescription(controlFont, logFont, defaultControlFontPixelSize); 314 } 315 } 316 fontDescription = controlFont; 317 break; 318 default: { // Everything else uses the stock GUI font. 319 if (!systemFont.isAbsoluteSize()) { 320 HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT); 321 if (hGDI) { 322 LOGFONT logFont; 323 if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0) 324 fillFontDescription(systemFont, logFont); 325 } 326 } 327 fontDescription = systemFont; 328 } 329 } 330} 331 332bool RenderThemeWin::supportsFocus(ControlPart appearance) const 333{ 334 switch (appearance) { 335 case PushButtonPart: 336 case ButtonPart: 337 case DefaultButtonPart: 338 return true; 339 default: 340 return false; 341 } 342} 343 344bool RenderThemeWin::supportsFocusRing(const RenderStyle* style) const 345{ 346 return supportsFocus(style->appearance()); 347} 348 349unsigned RenderThemeWin::determineClassicState(RenderObject* o) 350{ 351 unsigned state = 0; 352 switch (o->style()->appearance()) { 353 case PushButtonPart: 354 case ButtonPart: 355 case DefaultButtonPart: 356 state = DFCS_BUTTONPUSH; 357 if (!isEnabled(o)) 358 state |= DFCS_INACTIVE; 359 else if (isPressed(o)) 360 state |= DFCS_PUSHED; 361 break; 362 case RadioPart: 363 case CheckboxPart: 364 state = (o->style()->appearance() == RadioPart) ? DFCS_BUTTONRADIO : DFCS_BUTTONCHECK; 365 if (isChecked(o)) 366 state |= DFCS_CHECKED; 367 if (!isEnabled(o)) 368 state |= DFCS_INACTIVE; 369 else if (isPressed(o)) 370 state |= DFCS_PUSHED; 371 break; 372 case MenulistPart: 373 state = DFCS_SCROLLCOMBOBOX; 374 if (!isEnabled(o)) 375 state |= DFCS_INACTIVE; 376 else if (isPressed(o)) 377 state |= DFCS_PUSHED; 378 default: 379 break; 380 } 381 return state; 382} 383 384unsigned RenderThemeWin::determineState(RenderObject* o) 385{ 386 unsigned result = TS_NORMAL; 387 ControlPart appearance = o->style()->appearance(); 388 if (!isEnabled(o)) 389 result = TS_DISABLED; 390 else if (isReadOnlyControl(o) && (TextFieldPart == appearance || TextAreaPart == appearance || SearchFieldPart == appearance)) 391 result = TFS_READONLY; // Readonly is supported on textfields. 392 else if (isPressed(o)) // Active overrides hover and focused. 393 result = TS_ACTIVE; 394 else if (supportsFocus(appearance) && isFocused(o)) 395 result = TS_FOCUSED; 396 else if (isHovered(o)) 397 result = TS_HOVER; 398 if (isChecked(o)) 399 result += 4; // 4 unchecked states, 4 checked states. 400 return result; 401} 402 403unsigned RenderThemeWin::determineSliderThumbState(RenderObject* o) 404{ 405 unsigned result = TUS_NORMAL; 406 if (!isEnabled(o->parent())) 407 result = TUS_DISABLED; 408 else if (supportsFocus(o->style()->appearance()) && isFocused(o->parent())) 409 result = TUS_FOCUSED; 410 else if (static_cast<RenderSlider*>(o->parent())->inDragMode()) 411 result = TUS_PRESSED; 412 else if (isHovered(o)) 413 result = TUS_HOT; 414 return result; 415} 416 417unsigned RenderThemeWin::determineButtonState(RenderObject* o) 418{ 419 unsigned result = PBS_NORMAL; 420 if (!isEnabled(o)) 421 result = PBS_DISABLED; 422 else if (isPressed(o)) 423 result = PBS_PRESSED; 424 else if (supportsFocus(o->style()->appearance()) && isFocused(o)) 425 result = PBS_DEFAULTED; 426 else if (isHovered(o)) 427 result = PBS_HOT; 428 else if (isDefault(o)) 429 result = PBS_DEFAULTED; 430 return result; 431} 432 433ThemeData RenderThemeWin::getClassicThemeData(RenderObject* o) 434{ 435 ThemeData result; 436 switch (o->style()->appearance()) { 437 case PushButtonPart: 438 case ButtonPart: 439 case DefaultButtonPart: 440 case CheckboxPart: 441 case RadioPart: 442 result.m_part = DFC_BUTTON; 443 result.m_state = determineClassicState(o); 444 break; 445 case MenulistPart: 446 result.m_part = DFC_SCROLL; 447 result.m_state = determineClassicState(o); 448 break; 449 case SearchFieldPart: 450 case TextFieldPart: 451 case TextAreaPart: 452 result.m_part = TFP_TEXTFIELD; 453 result.m_state = determineState(o); 454 break; 455 case SliderHorizontalPart: 456 result.m_part = TKP_TRACK; 457 result.m_state = TS_NORMAL; 458 break; 459 case SliderVerticalPart: 460 result.m_part = TKP_TRACKVERT; 461 result.m_state = TS_NORMAL; 462 break; 463 case SliderThumbHorizontalPart: 464 result.m_part = TKP_THUMBBOTTOM; 465 result.m_state = determineSliderThumbState(o); 466 break; 467 case SliderThumbVerticalPart: 468 result.m_part = TKP_THUMBRIGHT; 469 result.m_state = determineSliderThumbState(o); 470 break; 471 default: 472 break; 473 } 474 return result; 475} 476 477ThemeData RenderThemeWin::getThemeData(RenderObject* o) 478{ 479 if (!haveTheme) 480 return getClassicThemeData(o); 481 482 ThemeData result; 483 switch (o->style()->appearance()) { 484 case PushButtonPart: 485 case ButtonPart: 486 case DefaultButtonPart: 487 result.m_part = BP_BUTTON; 488 result.m_state = determineButtonState(o); 489 break; 490 case CheckboxPart: 491 result.m_part = BP_CHECKBOX; 492 result.m_state = determineState(o); 493 break; 494 case MenulistPart: 495 case MenulistButtonPart: 496 result.m_part = CP_DROPDOWNBUTTON; 497 result.m_state = determineState(o); 498 break; 499 case RadioPart: 500 result.m_part = BP_RADIO; 501 result.m_state = determineState(o); 502 break; 503 case SearchFieldPart: 504 case TextFieldPart: 505 case TextAreaPart: 506 result.m_part = TFP_TEXTFIELD; 507 result.m_state = determineState(o); 508 break; 509 case SliderHorizontalPart: 510 result.m_part = TKP_TRACK; 511 result.m_state = TS_NORMAL; 512 break; 513 case SliderVerticalPart: 514 result.m_part = TKP_TRACKVERT; 515 result.m_state = TS_NORMAL; 516 break; 517 case SliderThumbHorizontalPart: 518 result.m_part = TKP_THUMBBOTTOM; 519 result.m_state = determineSliderThumbState(o); 520 break; 521 case SliderThumbVerticalPart: 522 result.m_part = TKP_THUMBRIGHT; 523 result.m_state = determineSliderThumbState(o); 524 break; 525 } 526 527 return result; 528} 529 530static void drawControl(GraphicsContext* context, RenderObject* o, HANDLE theme, const ThemeData& themeData, const IntRect& r) 531{ 532 bool alphaBlend = false; 533 if (theme) 534 alphaBlend = IsThemeBackgroundPartiallyTransparent(theme, themeData.m_part, themeData.m_state); 535 HDC hdc = context->getWindowsContext(r, alphaBlend); 536 RECT widgetRect = r; 537 if (theme) 538 DrawThemeBackground(theme, hdc, themeData.m_part, themeData.m_state, &widgetRect, NULL); 539 else { 540 if (themeData.m_part == TFP_TEXTFIELD) { 541 ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); 542 if (themeData.m_state == TS_DISABLED || themeData.m_state == TFS_READONLY) 543 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_BTNFACE+1)); 544 else 545 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_WINDOW+1)); 546 } else if (themeData.m_part == TKP_TRACK || themeData.m_part == TKP_TRACKVERT) { 547 ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); 548 ::FillRect(hdc, &widgetRect, (HBRUSH)GetStockObject(GRAY_BRUSH)); 549 } else if ((o->style()->appearance() == SliderThumbHorizontalPart || 550 o->style()->appearance() == SliderThumbVerticalPart) && 551 (themeData.m_part == TKP_THUMBBOTTOM || themeData.m_part == TKP_THUMBTOP || 552 themeData.m_part == TKP_THUMBLEFT || themeData.m_part == TKP_THUMBRIGHT)) { 553 ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE | BF_ADJUST); 554 if (themeData.m_state == TUS_DISABLED) { 555 static WORD patternBits[8] = {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55}; 556 HBITMAP patternBmp = ::CreateBitmap(8, 8, 1, 1, patternBits); 557 if (patternBmp) { 558 HBRUSH brush = (HBRUSH) ::CreatePatternBrush(patternBmp); 559 COLORREF oldForeColor = ::SetTextColor(hdc, ::GetSysColor(COLOR_3DFACE)); 560 COLORREF oldBackColor = ::SetBkColor(hdc, ::GetSysColor(COLOR_3DHILIGHT)); 561 POINT p; 562 ::GetViewportOrgEx(hdc, &p); 563 ::SetBrushOrgEx(hdc, p.x + widgetRect.left, p.y + widgetRect.top, NULL); 564 HBRUSH oldBrush = (HBRUSH) ::SelectObject(hdc, brush); 565 ::FillRect(hdc, &widgetRect, brush); 566 ::SetTextColor(hdc, oldForeColor); 567 ::SetBkColor(hdc, oldBackColor); 568 ::SelectObject(hdc, oldBrush); 569 ::DeleteObject(brush); 570 } else 571 ::FillRect(hdc, &widgetRect, (HBRUSH)COLOR_3DHILIGHT); 572 ::DeleteObject(patternBmp); 573 } 574 } else { 575 // Push buttons, buttons, checkboxes and radios, and the dropdown arrow in menulists. 576 if (o->style()->appearance() == DefaultButtonPart) { 577 HBRUSH brush = ::GetSysColorBrush(COLOR_3DDKSHADOW); 578 ::FrameRect(hdc, &widgetRect, brush); 579 ::InflateRect(&widgetRect, -1, -1); 580 ::DrawEdge(hdc, &widgetRect, BDR_RAISEDOUTER, BF_RECT | BF_MIDDLE); 581 } 582 ::DrawFrameControl(hdc, &widgetRect, themeData.m_part, themeData.m_state); 583 } 584 } 585 context->releaseWindowsContext(hdc, r, alphaBlend); 586} 587 588bool RenderThemeWin::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) 589{ 590 drawControl(i.context, o, buttonTheme(), getThemeData(o), r); 591 return false; 592} 593 594void RenderThemeWin::setCheckboxSize(RenderStyle* style) const 595{ 596 // If the width and height are both specified, then we have nothing to do. 597 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) 598 return; 599 600 // FIXME: A hard-coded size of 13 is used. This is wrong but necessary for now. It matches Firefox. 601 // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for 602 // the higher DPI. Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's 603 // metrics. 604 if (style->width().isIntrinsicOrAuto()) 605 style->setWidth(Length(13, Fixed)); 606 if (style->height().isAuto()) 607 style->setHeight(Length(13, Fixed)); 608} 609 610bool RenderThemeWin::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) 611{ 612 drawControl(i.context, o, textFieldTheme(), getThemeData(o), r); 613 return false; 614} 615 616bool RenderThemeWin::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) 617{ 618 // The outer box of a menu list is just a text field. Paint it first. 619 drawControl(i.context, o, textFieldTheme(), ThemeData(TFP_TEXTFIELD, determineState(o)), r); 620 621 return paintMenuListButton(o, i, r); 622} 623 624void RenderThemeWin::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 625{ 626 style->resetBorder(); 627 adjustMenuListButtonStyle(selector, style, e); 628} 629 630void RenderThemeWin::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 631{ 632 // These are the paddings needed to place the text correctly in the <select> box 633 const int dropDownBoxPaddingTop = 2; 634 const int dropDownBoxPaddingRight = style->direction() == LTR ? 4 + dropDownButtonWidth : 4; 635 const int dropDownBoxPaddingBottom = 2; 636 const int dropDownBoxPaddingLeft = style->direction() == LTR ? 4 : 4 + dropDownButtonWidth; 637 // The <select> box must be at least 12px high for the button to render nicely on Windows 638 const int dropDownBoxMinHeight = 12; 639 640 // Position the text correctly within the select box and make the box wide enough to fit the dropdown button 641 style->setPaddingTop(Length(dropDownBoxPaddingTop, Fixed)); 642 style->setPaddingRight(Length(dropDownBoxPaddingRight, Fixed)); 643 style->setPaddingBottom(Length(dropDownBoxPaddingBottom, Fixed)); 644 style->setPaddingLeft(Length(dropDownBoxPaddingLeft, Fixed)); 645 646 // Height is locked to auto 647 style->setHeight(Length(Auto)); 648 649 // Calculate our min-height 650 int minHeight = style->font().height(); 651 minHeight = max(minHeight, dropDownBoxMinHeight); 652 653 style->setMinHeight(Length(minHeight, Fixed)); 654 655 // White-space is locked to pre 656 style->setWhiteSpace(PRE); 657} 658 659bool RenderThemeWin::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) 660{ 661 // FIXME: Don't make hardcoded assumptions about the thickness of the textfield border. 662 int borderThickness = haveTheme ? 1 : 2; 663 664 // Paint the dropdown button on the inner edge of the text field, 665 // leaving space for the text field's 1px border 666 IntRect buttonRect(r); 667 buttonRect.inflate(-borderThickness); 668 if (o->style()->direction() == LTR) 669 buttonRect.setX(buttonRect.right() - dropDownButtonWidth); 670 buttonRect.setWidth(dropDownButtonWidth); 671 672 drawControl(i.context, o, menuListTheme(), getThemeData(o), buttonRect); 673 674 return false; 675} 676 677const int trackWidth = 4; 678 679bool RenderThemeWin::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) 680{ 681 IntRect bounds = r; 682 683 if (o->style()->appearance() == SliderHorizontalPart) { 684 bounds.setHeight(trackWidth); 685 bounds.setY(r.y() + r.height() / 2 - trackWidth / 2); 686 } else if (o->style()->appearance() == SliderVerticalPart) { 687 bounds.setWidth(trackWidth); 688 bounds.setX(r.x() + r.width() / 2 - trackWidth / 2); 689 } 690 691 drawControl(i.context, o, sliderTheme(), getThemeData(o), bounds); 692 return false; 693} 694 695bool RenderThemeWin::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) 696{ 697 drawControl(i.context, o, sliderTheme(), getThemeData(o), r); 698 return false; 699} 700 701const int sliderThumbWidth = 7; 702const int sliderThumbHeight = 15; 703 704void RenderThemeWin::adjustSliderThumbSize(RenderObject* o) const 705{ 706 if (o->style()->appearance() == SliderThumbVerticalPart) { 707 o->style()->setWidth(Length(sliderThumbHeight, Fixed)); 708 o->style()->setHeight(Length(sliderThumbWidth, Fixed)); 709 } else if (o->style()->appearance() == SliderThumbHorizontalPart) { 710 o->style()->setWidth(Length(sliderThumbWidth, Fixed)); 711 o->style()->setHeight(Length(sliderThumbHeight, Fixed)); 712 } 713} 714 715void RenderThemeWin::adjustButtonInnerStyle(RenderStyle* style) const 716{ 717 // This inner padding matches Firefox. 718 style->setPaddingTop(Length(1, Fixed)); 719 style->setPaddingRight(Length(3, Fixed)); 720 style->setPaddingBottom(Length(1, Fixed)); 721 style->setPaddingLeft(Length(3, Fixed)); 722} 723 724bool RenderThemeWin::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) 725{ 726 return paintTextField(o, i, r); 727} 728 729void RenderThemeWin::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 730{ 731 // Override padding size to match AppKit text positioning. 732 const int padding = 1; 733 style->setPaddingLeft(Length(padding, Fixed)); 734 style->setPaddingRight(Length(padding, Fixed)); 735 style->setPaddingTop(Length(padding, Fixed)); 736 style->setPaddingBottom(Length(padding, Fixed)); 737} 738 739bool RenderThemeWin::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 740{ 741 IntRect bounds = r; 742 ASSERT(o->parent()); 743 if (!o->parent() || !o->parent()->isBox()) 744 return false; 745 746 RenderBox* parentRenderBox = toRenderBox(o->parent()); 747 748 IntRect parentBox = parentRenderBox->absoluteContentBox(); 749 750 // Make sure the scaled button stays square and will fit in its parent's box 751 bounds.setHeight(min(parentBox.width(), min(parentBox.height(), bounds.height()))); 752 bounds.setWidth(bounds.height()); 753 754 // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will 755 // be one pixel closer to the bottom of the field. This tends to look better with the text. 756 bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); 757 758 static Image* cancelImage = Image::loadPlatformResource("searchCancel").releaseRef(); 759 static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").releaseRef(); 760 paintInfo.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, bounds); 761 return false; 762} 763 764void RenderThemeWin::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 765{ 766 // Scale the button size based on the font size 767 float fontScale = style->fontSize() / defaultControlFontPixelSize; 768 int cancelButtonSize = lroundf(min(max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize)); 769 style->setWidth(Length(cancelButtonSize, Fixed)); 770 style->setHeight(Length(cancelButtonSize, Fixed)); 771} 772 773void RenderThemeWin::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 774{ 775 IntSize emptySize(1, 11); 776 style->setWidth(Length(emptySize.width(), Fixed)); 777 style->setHeight(Length(emptySize.height(), Fixed)); 778} 779 780void RenderThemeWin::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 781{ 782 // Scale the decoration size based on the font size 783 float fontScale = style->fontSize() / defaultControlFontPixelSize; 784 int magnifierSize = lroundf(min(max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale), 785 maxSearchFieldResultsDecorationSize)); 786 style->setWidth(Length(magnifierSize, Fixed)); 787 style->setHeight(Length(magnifierSize, Fixed)); 788} 789 790bool RenderThemeWin::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 791{ 792 IntRect bounds = r; 793 ASSERT(o->parent()); 794 if (!o->parent() || !o->parent()->isBox()) 795 return false; 796 797 RenderBox* parentRenderBox = toRenderBox(o->parent()); 798 IntRect parentBox = parentRenderBox->absoluteContentBox(); 799 800 // Make sure the scaled decoration stays square and will fit in its parent's box 801 bounds.setHeight(min(parentBox.width(), min(parentBox.height(), bounds.height()))); 802 bounds.setWidth(bounds.height()); 803 804 // Center the decoration vertically. Round up though, so if it has to be one pixel off-center, it will 805 // be one pixel closer to the bottom of the field. This tends to look better with the text. 806 bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); 807 808 static Image* magnifierImage = Image::loadPlatformResource("searchMagnifier").releaseRef(); 809 paintInfo.context->drawImage(magnifierImage, bounds); 810 return false; 811} 812 813void RenderThemeWin::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 814{ 815 // Scale the button size based on the font size 816 float fontScale = style->fontSize() / defaultControlFontPixelSize; 817 int magnifierHeight = lroundf(min(max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale), 818 maxSearchFieldResultsDecorationSize)); 819 int magnifierWidth = lroundf(magnifierHeight * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize); 820 style->setWidth(Length(magnifierWidth, Fixed)); 821 style->setHeight(Length(magnifierHeight, Fixed)); 822} 823 824bool RenderThemeWin::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 825{ 826 IntRect bounds = r; 827 ASSERT(o->parent()); 828 if (!o->parent()) 829 return false; 830 if (!o->parent() || !o->parent()->isBox()) 831 return false; 832 833 RenderBox* parentRenderBox = toRenderBox(o->parent()); 834 IntRect parentBox = parentRenderBox->absoluteContentBox(); 835 836 // Make sure the scaled decoration will fit in its parent's box 837 bounds.setHeight(min(parentBox.height(), bounds.height())); 838 bounds.setWidth(min(parentBox.width(), static_cast<int>(bounds.height() * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize))); 839 840 // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will 841 // be one pixel closer to the bottom of the field. This tends to look better with the text. 842 bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); 843 844 static Image* magnifierImage = Image::loadPlatformResource("searchMagnifierResults").releaseRef(); 845 paintInfo.context->drawImage(magnifierImage, bounds); 846 return false; 847} 848 849// Map a CSSValue* system color to an index understood by GetSysColor 850static int cssValueIdToSysColorIndex(int cssValueId) 851{ 852 switch (cssValueId) { 853 case CSSValueActiveborder: return COLOR_ACTIVEBORDER; 854 case CSSValueActivecaption: return COLOR_ACTIVECAPTION; 855 case CSSValueAppworkspace: return COLOR_APPWORKSPACE; 856 case CSSValueBackground: return COLOR_BACKGROUND; 857 case CSSValueButtonface: return COLOR_BTNFACE; 858 case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT; 859 case CSSValueButtonshadow: return COLOR_BTNSHADOW; 860 case CSSValueButtontext: return COLOR_BTNTEXT; 861 case CSSValueCaptiontext: return COLOR_CAPTIONTEXT; 862 case CSSValueGraytext: return COLOR_GRAYTEXT; 863 case CSSValueHighlight: return COLOR_HIGHLIGHT; 864 case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT; 865 case CSSValueInactiveborder: return COLOR_INACTIVEBORDER; 866 case CSSValueInactivecaption: return COLOR_INACTIVECAPTION; 867 case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT; 868 case CSSValueInfobackground: return COLOR_INFOBK; 869 case CSSValueInfotext: return COLOR_INFOTEXT; 870 case CSSValueMenu: return COLOR_MENU; 871 case CSSValueMenutext: return COLOR_MENUTEXT; 872 case CSSValueScrollbar: return COLOR_SCROLLBAR; 873 case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW; 874 case CSSValueThreedface: return COLOR_3DFACE; 875 case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT; 876 case CSSValueThreedlightshadow: return COLOR_3DLIGHT; 877 case CSSValueThreedshadow: return COLOR_3DSHADOW; 878 case CSSValueWindow: return COLOR_WINDOW; 879 case CSSValueWindowframe: return COLOR_WINDOWFRAME; 880 case CSSValueWindowtext: return COLOR_WINDOWTEXT; 881 default: return -1; // Unsupported CSSValue 882 } 883} 884 885Color RenderThemeWin::systemColor(int cssValueId) const 886{ 887 int sysColorIndex = cssValueIdToSysColorIndex(cssValueId); 888 if (sysColorIndex == -1) 889 return RenderTheme::systemColor(cssValueId); 890 891 COLORREF color = GetSysColor(sysColorIndex); 892 return Color(GetRValue(color), GetGValue(color), GetBValue(color)); 893} 894 895} 896