/* * Copyright (C) 2006, 2007 Apple Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * */ #include "config.h" #include "RenderThemeWin.h" #include "CSSStyleSheet.h" #include "CSSValueKeywords.h" #include "Document.h" #include "GraphicsContext.h" #include "HTMLElement.h" #include "HTMLSelectElement.h" #include "Icon.h" #include "RenderSlider.h" #include "SoftLinking.h" #include "UserAgentStyleSheets.h" #include <tchar.h> /* * The following constants are used to determine how a widget is drawn using * Windows' Theme API. For more information on theme parts and states see * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp */ // Generic state constants #define TS_NORMAL 1 #define TS_HOVER 2 #define TS_ACTIVE 3 #define TS_DISABLED 4 #define TS_FOCUSED 5 // Button constants #define BP_BUTTON 1 #define BP_RADIO 2 #define BP_CHECKBOX 3 // Textfield constants #define TFP_TEXTFIELD 1 #define TFS_READONLY 6 // ComboBox constants (from tmschema.h) #define CP_DROPDOWNBUTTON 1 // TrackBar (slider) parts #define TKP_TRACK 1 #define TKP_TRACKVERT 2 // TrackBar (slider) thumb parts #define TKP_THUMBBOTTOM 4 #define TKP_THUMBTOP 5 #define TKP_THUMBLEFT 7 #define TKP_THUMBRIGHT 8 // Trackbar (slider) thumb states #define TUS_NORMAL 1 #define TUS_HOT 2 #define TUS_PRESSED 3 #define TUS_FOCUSED 4 #define TUS_DISABLED 5 // button states #define PBS_NORMAL 1 #define PBS_HOT 2 #define PBS_PRESSED 3 #define PBS_DISABLED 4 #define PBS_DEFAULTED 5 SOFT_LINK_LIBRARY(uxtheme) SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (hwnd, pszClassList)) SOFT_LINK(uxtheme, CloseThemeData, HRESULT, WINAPI, (HANDLE hTheme), (hTheme)) SOFT_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)) SOFT_LINK(uxtheme, IsThemeActive, BOOL, WINAPI, (), ()) SOFT_LINK(uxtheme, IsThemeBackgroundPartiallyTransparent, BOOL, WINAPI, (HANDLE hTheme, int iPartId, int iStateId), (hTheme, iPartId, iStateId)) static bool haveTheme; using namespace std; namespace WebCore { // This is the fixed width IE and Firefox use for buttons on dropdown menus static const int dropDownButtonWidth = 17; static const int shell32MagnifierIconIndex = 22; // Default font size to match Firefox. static const float defaultControlFontPixelSize = 13; static const float defaultCancelButtonSize = 9; static const float minCancelButtonSize = 5; static const float maxCancelButtonSize = 21; static const float defaultSearchFieldResultsDecorationSize = 13; static const float minSearchFieldResultsDecorationSize = 9; static const float maxSearchFieldResultsDecorationSize = 30; static const float defaultSearchFieldResultsButtonWidth = 18; static bool gWebKitIsBeingUnloaded; void RenderThemeWin::setWebKitIsBeingUnloaded() { gWebKitIsBeingUnloaded = true; } #if !USE(SAFARI_THEME) RenderTheme* theme() { static RenderThemeWin winTheme; return &winTheme; } #endif RenderThemeWin::RenderThemeWin() : m_buttonTheme(0) , m_textFieldTheme(0) , m_menuListTheme(0) , m_sliderTheme(0) { haveTheme = uxthemeLibrary() && IsThemeActive(); } RenderThemeWin::~RenderThemeWin() { // If WebKit is being unloaded, then uxtheme.dll is no longer available. if (gWebKitIsBeingUnloaded || !uxthemeLibrary()) return; close(); } HANDLE RenderThemeWin::buttonTheme() const { if (haveTheme && !m_buttonTheme) m_buttonTheme = OpenThemeData(0, L"Button"); return m_buttonTheme; } HANDLE RenderThemeWin::textFieldTheme() const { if (haveTheme && !m_textFieldTheme) m_textFieldTheme = OpenThemeData(0, L"Edit"); return m_textFieldTheme; } HANDLE RenderThemeWin::menuListTheme() const { if (haveTheme && !m_menuListTheme) m_menuListTheme = OpenThemeData(0, L"ComboBox"); return m_menuListTheme; } HANDLE RenderThemeWin::sliderTheme() const { if (haveTheme && !m_sliderTheme) m_sliderTheme = OpenThemeData(0, L"TrackBar"); return m_sliderTheme; } void RenderThemeWin::close() { // This method will need to be called when the OS theme changes to flush our cached themes. if (m_buttonTheme) CloseThemeData(m_buttonTheme); if (m_textFieldTheme) CloseThemeData(m_textFieldTheme); if (m_menuListTheme) CloseThemeData(m_menuListTheme); if (m_sliderTheme) CloseThemeData(m_sliderTheme); m_buttonTheme = m_textFieldTheme = m_menuListTheme = m_sliderTheme = 0; haveTheme = uxthemeLibrary() && IsThemeActive(); } void RenderThemeWin::themeChanged() { close(); } String RenderThemeWin::extraDefaultStyleSheet() { return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet)); } String RenderThemeWin::extraQuirksStyleSheet() { return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet)); } bool RenderThemeWin::supportsHover(const RenderStyle*) const { // The Classic/2k look has no hover effects. return haveTheme; } Color RenderThemeWin::platformActiveSelectionBackgroundColor() const { COLORREF color = GetSysColor(COLOR_HIGHLIGHT); return Color(GetRValue(color), GetGValue(color), GetBValue(color)); } Color RenderThemeWin::platformInactiveSelectionBackgroundColor() const { // This color matches Firefox. return Color(176, 176, 176); } Color RenderThemeWin::platformActiveSelectionForegroundColor() const { COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT); return Color(GetRValue(color), GetGValue(color), GetBValue(color)); } Color RenderThemeWin::platformInactiveSelectionForegroundColor() const { return platformActiveSelectionForegroundColor(); } static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont, float fontSize) { fontDescription.setIsAbsoluteSize(true); fontDescription.setGenericFamily(FontDescription::NoFamily); fontDescription.firstFamily().setFamily(String(logFont.lfFaceName)); fontDescription.setSpecifiedSize(fontSize); fontDescription.setWeight(logFont.lfWeight >= 700 ? FontWeightBold : FontWeightNormal); // FIXME: Use real weight. fontDescription.setItalic(logFont.lfItalic); } static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont) { fillFontDescription(fontDescription, logFont, abs(logFont.lfHeight)); } void RenderThemeWin::systemFont(int propId, FontDescription& fontDescription) const { static FontDescription captionFont; static FontDescription controlFont; static FontDescription smallCaptionFont; static FontDescription menuFont; static FontDescription iconFont; static FontDescription messageBoxFont; static FontDescription statusBarFont; static FontDescription systemFont; static bool initialized; static NONCLIENTMETRICS ncm; if (!initialized) { initialized = true; ncm.cbSize = sizeof(NONCLIENTMETRICS); ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0); } switch (propId) { case CSSValueIcon: { if (!iconFont.isAbsoluteSize()) { LOGFONT logFont; ::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(logFont), &logFont, 0); fillFontDescription(iconFont, logFont); } fontDescription = iconFont; break; } case CSSValueMenu: if (!menuFont.isAbsoluteSize()) fillFontDescription(menuFont, ncm.lfMenuFont); fontDescription = menuFont; break; case CSSValueMessageBox: if (!messageBoxFont.isAbsoluteSize()) fillFontDescription(messageBoxFont, ncm.lfMessageFont); fontDescription = messageBoxFont; break; case CSSValueStatusBar: if (!statusBarFont.isAbsoluteSize()) fillFontDescription(statusBarFont, ncm.lfStatusFont); fontDescription = statusBarFont; break; case CSSValueCaption: if (!captionFont.isAbsoluteSize()) fillFontDescription(captionFont, ncm.lfCaptionFont); fontDescription = captionFont; break; case CSSValueSmallCaption: if (!smallCaptionFont.isAbsoluteSize()) fillFontDescription(smallCaptionFont, ncm.lfSmCaptionFont); fontDescription = smallCaptionFont; break; case CSSValueWebkitSmallControl: case CSSValueWebkitMiniControl: // Just map to small. case CSSValueWebkitControl: // Just map to small. if (!controlFont.isAbsoluteSize()) { HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT); if (hGDI) { LOGFONT logFont; if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0) fillFontDescription(controlFont, logFont, defaultControlFontPixelSize); } } fontDescription = controlFont; break; default: { // Everything else uses the stock GUI font. if (!systemFont.isAbsoluteSize()) { HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT); if (hGDI) { LOGFONT logFont; if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0) fillFontDescription(systemFont, logFont); } } fontDescription = systemFont; } } } bool RenderThemeWin::supportsFocus(ControlPart appearance) const { switch (appearance) { case PushButtonPart: case ButtonPart: case DefaultButtonPart: return true; default: return false; } } bool RenderThemeWin::supportsFocusRing(const RenderStyle* style) const { return supportsFocus(style->appearance()); } unsigned RenderThemeWin::determineClassicState(RenderObject* o) { unsigned state = 0; switch (o->style()->appearance()) { case PushButtonPart: case ButtonPart: case DefaultButtonPart: state = DFCS_BUTTONPUSH; if (!isEnabled(o)) state |= DFCS_INACTIVE; else if (isPressed(o)) state |= DFCS_PUSHED; break; case RadioPart: case CheckboxPart: state = (o->style()->appearance() == RadioPart) ? DFCS_BUTTONRADIO : DFCS_BUTTONCHECK; if (isChecked(o)) state |= DFCS_CHECKED; if (!isEnabled(o)) state |= DFCS_INACTIVE; else if (isPressed(o)) state |= DFCS_PUSHED; break; case MenulistPart: state = DFCS_SCROLLCOMBOBOX; if (!isEnabled(o)) state |= DFCS_INACTIVE; else if (isPressed(o)) state |= DFCS_PUSHED; default: break; } return state; } unsigned RenderThemeWin::determineState(RenderObject* o) { unsigned result = TS_NORMAL; ControlPart appearance = o->style()->appearance(); if (!isEnabled(o)) result = TS_DISABLED; else if (isReadOnlyControl(o) && (TextFieldPart == appearance || TextAreaPart == appearance || SearchFieldPart == appearance)) result = TFS_READONLY; // Readonly is supported on textfields. else if (isPressed(o)) // Active overrides hover and focused. result = TS_ACTIVE; else if (supportsFocus(appearance) && isFocused(o)) result = TS_FOCUSED; else if (isHovered(o)) result = TS_HOVER; if (isChecked(o)) result += 4; // 4 unchecked states, 4 checked states. return result; } unsigned RenderThemeWin::determineSliderThumbState(RenderObject* o) { unsigned result = TUS_NORMAL; if (!isEnabled(o->parent())) result = TUS_DISABLED; else if (supportsFocus(o->style()->appearance()) && isFocused(o->parent())) result = TUS_FOCUSED; else if (static_cast<RenderSlider*>(o->parent())->inDragMode()) result = TUS_PRESSED; else if (isHovered(o)) result = TUS_HOT; return result; } unsigned RenderThemeWin::determineButtonState(RenderObject* o) { unsigned result = PBS_NORMAL; if (!isEnabled(o)) result = PBS_DISABLED; else if (isPressed(o)) result = PBS_PRESSED; else if (supportsFocus(o->style()->appearance()) && isFocused(o)) result = PBS_DEFAULTED; else if (isHovered(o)) result = PBS_HOT; else if (isDefault(o)) result = PBS_DEFAULTED; return result; } ThemeData RenderThemeWin::getClassicThemeData(RenderObject* o) { ThemeData result; switch (o->style()->appearance()) { case PushButtonPart: case ButtonPart: case DefaultButtonPart: case CheckboxPart: case RadioPart: result.m_part = DFC_BUTTON; result.m_state = determineClassicState(o); break; case MenulistPart: result.m_part = DFC_SCROLL; result.m_state = determineClassicState(o); break; case SearchFieldPart: case TextFieldPart: case TextAreaPart: result.m_part = TFP_TEXTFIELD; result.m_state = determineState(o); break; case SliderHorizontalPart: result.m_part = TKP_TRACK; result.m_state = TS_NORMAL; break; case SliderVerticalPart: result.m_part = TKP_TRACKVERT; result.m_state = TS_NORMAL; break; case SliderThumbHorizontalPart: result.m_part = TKP_THUMBBOTTOM; result.m_state = determineSliderThumbState(o); break; case SliderThumbVerticalPart: result.m_part = TKP_THUMBRIGHT; result.m_state = determineSliderThumbState(o); break; default: break; } return result; } ThemeData RenderThemeWin::getThemeData(RenderObject* o) { if (!haveTheme) return getClassicThemeData(o); ThemeData result; switch (o->style()->appearance()) { case PushButtonPart: case ButtonPart: case DefaultButtonPart: result.m_part = BP_BUTTON; result.m_state = determineButtonState(o); break; case CheckboxPart: result.m_part = BP_CHECKBOX; result.m_state = determineState(o); break; case MenulistPart: case MenulistButtonPart: result.m_part = CP_DROPDOWNBUTTON; result.m_state = determineState(o); break; case RadioPart: result.m_part = BP_RADIO; result.m_state = determineState(o); break; case SearchFieldPart: case TextFieldPart: case TextAreaPart: result.m_part = TFP_TEXTFIELD; result.m_state = determineState(o); break; case SliderHorizontalPart: result.m_part = TKP_TRACK; result.m_state = TS_NORMAL; break; case SliderVerticalPart: result.m_part = TKP_TRACKVERT; result.m_state = TS_NORMAL; break; case SliderThumbHorizontalPart: result.m_part = TKP_THUMBBOTTOM; result.m_state = determineSliderThumbState(o); break; case SliderThumbVerticalPart: result.m_part = TKP_THUMBRIGHT; result.m_state = determineSliderThumbState(o); break; } return result; } static void drawControl(GraphicsContext* context, RenderObject* o, HANDLE theme, const ThemeData& themeData, const IntRect& r) { bool alphaBlend = false; if (theme) alphaBlend = IsThemeBackgroundPartiallyTransparent(theme, themeData.m_part, themeData.m_state); HDC hdc = context->getWindowsContext(r, alphaBlend); RECT widgetRect = r; if (theme) DrawThemeBackground(theme, hdc, themeData.m_part, themeData.m_state, &widgetRect, NULL); else { if (themeData.m_part == TFP_TEXTFIELD) { ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); if (themeData.m_state == TS_DISABLED || themeData.m_state == TFS_READONLY) ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_BTNFACE+1)); else ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_WINDOW+1)); } else if (themeData.m_part == TKP_TRACK || themeData.m_part == TKP_TRACKVERT) { ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); ::FillRect(hdc, &widgetRect, (HBRUSH)GetStockObject(GRAY_BRUSH)); } else if ((o->style()->appearance() == SliderThumbHorizontalPart || o->style()->appearance() == SliderThumbVerticalPart) && (themeData.m_part == TKP_THUMBBOTTOM || themeData.m_part == TKP_THUMBTOP || themeData.m_part == TKP_THUMBLEFT || themeData.m_part == TKP_THUMBRIGHT)) { ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE | BF_ADJUST); if (themeData.m_state == TUS_DISABLED) { static WORD patternBits[8] = {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55}; HBITMAP patternBmp = ::CreateBitmap(8, 8, 1, 1, patternBits); if (patternBmp) { HBRUSH brush = (HBRUSH) ::CreatePatternBrush(patternBmp); COLORREF oldForeColor = ::SetTextColor(hdc, ::GetSysColor(COLOR_3DFACE)); COLORREF oldBackColor = ::SetBkColor(hdc, ::GetSysColor(COLOR_3DHILIGHT)); POINT p; ::GetViewportOrgEx(hdc, &p); ::SetBrushOrgEx(hdc, p.x + widgetRect.left, p.y + widgetRect.top, NULL); HBRUSH oldBrush = (HBRUSH) ::SelectObject(hdc, brush); ::FillRect(hdc, &widgetRect, brush); ::SetTextColor(hdc, oldForeColor); ::SetBkColor(hdc, oldBackColor); ::SelectObject(hdc, oldBrush); ::DeleteObject(brush); } else ::FillRect(hdc, &widgetRect, (HBRUSH)COLOR_3DHILIGHT); ::DeleteObject(patternBmp); } } else { // Push buttons, buttons, checkboxes and radios, and the dropdown arrow in menulists. if (o->style()->appearance() == DefaultButtonPart) { HBRUSH brush = ::GetSysColorBrush(COLOR_3DDKSHADOW); ::FrameRect(hdc, &widgetRect, brush); ::InflateRect(&widgetRect, -1, -1); ::DrawEdge(hdc, &widgetRect, BDR_RAISEDOUTER, BF_RECT | BF_MIDDLE); } ::DrawFrameControl(hdc, &widgetRect, themeData.m_part, themeData.m_state); } } context->releaseWindowsContext(hdc, r, alphaBlend); } bool RenderThemeWin::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { drawControl(i.context, o, buttonTheme(), getThemeData(o), r); return false; } void RenderThemeWin::setCheckboxSize(RenderStyle* style) const { // If the width and height are both specified, then we have nothing to do. if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) return; // FIXME: A hard-coded size of 13 is used. This is wrong but necessary for now. It matches Firefox. // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for // the higher DPI. Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's // metrics. if (style->width().isIntrinsicOrAuto()) style->setWidth(Length(13, Fixed)); if (style->height().isAuto()) style->setHeight(Length(13, Fixed)); } bool RenderThemeWin::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { drawControl(i.context, o, textFieldTheme(), getThemeData(o), r); return false; } bool RenderThemeWin::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { // The outer box of a menu list is just a text field. Paint it first. drawControl(i.context, o, textFieldTheme(), ThemeData(TFP_TEXTFIELD, determineState(o)), r); return paintMenuListButton(o, i, r); } void RenderThemeWin::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const { style->resetBorder(); adjustMenuListButtonStyle(selector, style, e); } void RenderThemeWin::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const { // These are the paddings needed to place the text correctly in the <select> box const int dropDownBoxPaddingTop = 2; const int dropDownBoxPaddingRight = style->direction() == LTR ? 4 + dropDownButtonWidth : 4; const int dropDownBoxPaddingBottom = 2; const int dropDownBoxPaddingLeft = style->direction() == LTR ? 4 : 4 + dropDownButtonWidth; // The <select> box must be at least 12px high for the button to render nicely on Windows const int dropDownBoxMinHeight = 12; // Position the text correctly within the select box and make the box wide enough to fit the dropdown button style->setPaddingTop(Length(dropDownBoxPaddingTop, Fixed)); style->setPaddingRight(Length(dropDownBoxPaddingRight, Fixed)); style->setPaddingBottom(Length(dropDownBoxPaddingBottom, Fixed)); style->setPaddingLeft(Length(dropDownBoxPaddingLeft, Fixed)); // Height is locked to auto style->setHeight(Length(Auto)); // Calculate our min-height int minHeight = style->font().height(); minHeight = max(minHeight, dropDownBoxMinHeight); style->setMinHeight(Length(minHeight, Fixed)); // White-space is locked to pre style->setWhiteSpace(PRE); } bool RenderThemeWin::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { // FIXME: Don't make hardcoded assumptions about the thickness of the textfield border. int borderThickness = haveTheme ? 1 : 2; // Paint the dropdown button on the inner edge of the text field, // leaving space for the text field's 1px border IntRect buttonRect(r); buttonRect.inflate(-borderThickness); if (o->style()->direction() == LTR) buttonRect.setX(buttonRect.right() - dropDownButtonWidth); buttonRect.setWidth(dropDownButtonWidth); drawControl(i.context, o, menuListTheme(), getThemeData(o), buttonRect); return false; } const int trackWidth = 4; bool RenderThemeWin::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { IntRect bounds = r; if (o->style()->appearance() == SliderHorizontalPart) { bounds.setHeight(trackWidth); bounds.setY(r.y() + r.height() / 2 - trackWidth / 2); } else if (o->style()->appearance() == SliderVerticalPart) { bounds.setWidth(trackWidth); bounds.setX(r.x() + r.width() / 2 - trackWidth / 2); } drawControl(i.context, o, sliderTheme(), getThemeData(o), bounds); return false; } bool RenderThemeWin::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { drawControl(i.context, o, sliderTheme(), getThemeData(o), r); return false; } const int sliderThumbWidth = 7; const int sliderThumbHeight = 15; void RenderThemeWin::adjustSliderThumbSize(RenderObject* o) const { if (o->style()->appearance() == SliderThumbVerticalPart) { o->style()->setWidth(Length(sliderThumbHeight, Fixed)); o->style()->setHeight(Length(sliderThumbWidth, Fixed)); } else if (o->style()->appearance() == SliderThumbHorizontalPart) { o->style()->setWidth(Length(sliderThumbWidth, Fixed)); o->style()->setHeight(Length(sliderThumbHeight, Fixed)); } } void RenderThemeWin::adjustButtonInnerStyle(RenderStyle* style) const { // This inner padding matches Firefox. style->setPaddingTop(Length(1, Fixed)); style->setPaddingRight(Length(3, Fixed)); style->setPaddingBottom(Length(1, Fixed)); style->setPaddingLeft(Length(3, Fixed)); } bool RenderThemeWin::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { return paintTextField(o, i, r); } void RenderThemeWin::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const { // Override padding size to match AppKit text positioning. const int padding = 1; style->setPaddingLeft(Length(padding, Fixed)); style->setPaddingRight(Length(padding, Fixed)); style->setPaddingTop(Length(padding, Fixed)); style->setPaddingBottom(Length(padding, Fixed)); } bool RenderThemeWin::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { IntRect bounds = r; ASSERT(o->parent()); if (!o->parent() || !o->parent()->isBox()) return false; RenderBox* parentRenderBox = toRenderBox(o->parent()); IntRect parentBox = parentRenderBox->absoluteContentBox(); // Make sure the scaled button stays square and will fit in its parent's box bounds.setHeight(min(parentBox.width(), min(parentBox.height(), bounds.height()))); bounds.setWidth(bounds.height()); // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will // be one pixel closer to the bottom of the field. This tends to look better with the text. bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); static Image* cancelImage = Image::loadPlatformResource("searchCancel").releaseRef(); static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").releaseRef(); paintInfo.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, bounds); return false; } void RenderThemeWin::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const { // Scale the button size based on the font size float fontScale = style->fontSize() / defaultControlFontPixelSize; int cancelButtonSize = lroundf(min(max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize)); style->setWidth(Length(cancelButtonSize, Fixed)); style->setHeight(Length(cancelButtonSize, Fixed)); } void RenderThemeWin::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const { IntSize emptySize(1, 11); style->setWidth(Length(emptySize.width(), Fixed)); style->setHeight(Length(emptySize.height(), Fixed)); } void RenderThemeWin::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const { // Scale the decoration size based on the font size float fontScale = style->fontSize() / defaultControlFontPixelSize; int magnifierSize = lroundf(min(max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale), maxSearchFieldResultsDecorationSize)); style->setWidth(Length(magnifierSize, Fixed)); style->setHeight(Length(magnifierSize, Fixed)); } bool RenderThemeWin::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { IntRect bounds = r; ASSERT(o->parent()); if (!o->parent() || !o->parent()->isBox()) return false; RenderBox* parentRenderBox = toRenderBox(o->parent()); IntRect parentBox = parentRenderBox->absoluteContentBox(); // Make sure the scaled decoration stays square and will fit in its parent's box bounds.setHeight(min(parentBox.width(), min(parentBox.height(), bounds.height()))); bounds.setWidth(bounds.height()); // Center the decoration vertically. Round up though, so if it has to be one pixel off-center, it will // be one pixel closer to the bottom of the field. This tends to look better with the text. bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); static Image* magnifierImage = Image::loadPlatformResource("searchMagnifier").releaseRef(); paintInfo.context->drawImage(magnifierImage, bounds); return false; } void RenderThemeWin::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const { // Scale the button size based on the font size float fontScale = style->fontSize() / defaultControlFontPixelSize; int magnifierHeight = lroundf(min(max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale), maxSearchFieldResultsDecorationSize)); int magnifierWidth = lroundf(magnifierHeight * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize); style->setWidth(Length(magnifierWidth, Fixed)); style->setHeight(Length(magnifierHeight, Fixed)); } bool RenderThemeWin::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { IntRect bounds = r; ASSERT(o->parent()); if (!o->parent()) return false; if (!o->parent() || !o->parent()->isBox()) return false; RenderBox* parentRenderBox = toRenderBox(o->parent()); IntRect parentBox = parentRenderBox->absoluteContentBox(); // Make sure the scaled decoration will fit in its parent's box bounds.setHeight(min(parentBox.height(), bounds.height())); bounds.setWidth(min(parentBox.width(), static_cast<int>(bounds.height() * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize))); // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will // be one pixel closer to the bottom of the field. This tends to look better with the text. bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); static Image* magnifierImage = Image::loadPlatformResource("searchMagnifierResults").releaseRef(); paintInfo.context->drawImage(magnifierImage, bounds); return false; } // Map a CSSValue* system color to an index understood by GetSysColor static int cssValueIdToSysColorIndex(int cssValueId) { switch (cssValueId) { case CSSValueActiveborder: return COLOR_ACTIVEBORDER; case CSSValueActivecaption: return COLOR_ACTIVECAPTION; case CSSValueAppworkspace: return COLOR_APPWORKSPACE; case CSSValueBackground: return COLOR_BACKGROUND; case CSSValueButtonface: return COLOR_BTNFACE; case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT; case CSSValueButtonshadow: return COLOR_BTNSHADOW; case CSSValueButtontext: return COLOR_BTNTEXT; case CSSValueCaptiontext: return COLOR_CAPTIONTEXT; case CSSValueGraytext: return COLOR_GRAYTEXT; case CSSValueHighlight: return COLOR_HIGHLIGHT; case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT; case CSSValueInactiveborder: return COLOR_INACTIVEBORDER; case CSSValueInactivecaption: return COLOR_INACTIVECAPTION; case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT; case CSSValueInfobackground: return COLOR_INFOBK; case CSSValueInfotext: return COLOR_INFOTEXT; case CSSValueMenu: return COLOR_MENU; case CSSValueMenutext: return COLOR_MENUTEXT; case CSSValueScrollbar: return COLOR_SCROLLBAR; case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW; case CSSValueThreedface: return COLOR_3DFACE; case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT; case CSSValueThreedlightshadow: return COLOR_3DLIGHT; case CSSValueThreedshadow: return COLOR_3DSHADOW; case CSSValueWindow: return COLOR_WINDOW; case CSSValueWindowframe: return COLOR_WINDOWFRAME; case CSSValueWindowtext: return COLOR_WINDOWTEXT; default: return -1; // Unsupported CSSValue } } Color RenderThemeWin::systemColor(int cssValueId) const { int sysColorIndex = cssValueIdToSysColorIndex(cssValueId); if (sysColorIndex == -1) return RenderTheme::systemColor(cssValueId); COLORREF color = GetSysColor(sysColorIndex); return Color(GetRValue(color), GetGValue(color), GetBValue(color)); } }