summaryrefslogtreecommitdiffstats
path: root/ui/native_theme/native_theme_win.cc
diff options
context:
space:
mode:
authortfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-16 13:31:18 +0000
committertfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-16 13:31:18 +0000
commit990e62268f8a7074995ea00895da064312e49535 (patch)
tree6447bc15cd3e2f728b7351b27571a3c6da35ae41 /ui/native_theme/native_theme_win.cc
parent8d0f9016803d12e160b588cca2a7fc4612323aad (diff)
downloadchromium_src-990e62268f8a7074995ea00895da064312e49535.zip
chromium_src-990e62268f8a7074995ea00895da064312e49535.tar.gz
chromium_src-990e62268f8a7074995ea00895da064312e49535.tar.bz2
ui: Make native_theme its own library.
BUG=103304 R=sky@chromium.org Review URL: https://chromiumcodereview.appspot.com/11275322 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@168194 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/native_theme/native_theme_win.cc')
-rw-r--r--ui/native_theme/native_theme_win.cc1944
1 files changed, 1944 insertions, 0 deletions
diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc
new file mode 100644
index 0000000..d83f396
--- /dev/null
+++ b/ui/native_theme/native_theme_win.cc
@@ -0,0 +1,1944 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/native_theme/native_theme_win.h"
+
+#include <windows.h>
+#include <uxtheme.h>
+#include <vsstyle.h>
+#include <vssym32.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_handle.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/scoped_gdi_object.h"
+#include "base/win/scoped_hdc.h"
+#include "base/win/scoped_select_object.h"
+#include "base/win/windows_version.h"
+#include "skia/ext/bitmap_platform_device.h"
+#include "skia/ext/platform_canvas.h"
+#include "skia/ext/skia_utils_win.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColorPriv.h"
+#include "third_party/skia/include/core/SkShader.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/gfx/gdi_util.h"
+#include "ui/gfx/rect.h"
+
+namespace {
+
+// TODO: Obtain the correct colors using GetSysColor.
+// Theme colors returned by GetSystemColor().
+const SkColor kInvalidColorIdColor = SkColorSetRGB(255, 0, 128);
+// Dialogs:
+const SkColor kDialogBackgroundColor = SkColorSetRGB(200, 200, 200);
+// FocusableBorder:
+const SkColor kFocusedBorderColor = SkColorSetRGB(0x4D, 0x90, 0xFE);
+const SkColor kUnfocusedBorderColor = SkColorSetRGB(0xD9, 0xD9, 0xD9);
+// TextButton:
+const SkColor kTextButtonBackgroundColor = SkColorSetRGB(0xde, 0xde, 0xde);
+const SkColor kTextButtonEnabledColor = SkColorSetRGB(6, 45, 117);
+const SkColor kTextButtonDisabledColor = SkColorSetRGB(161, 161, 146);
+const SkColor kTextButtonHighlightColor = SkColorSetARGB(200, 255, 255, 255);
+const SkColor kTextButtonHoverColor = kTextButtonEnabledColor;
+// MenuItem:
+const SkColor kEnabledMenuItemForegroundColor = kTextButtonEnabledColor;
+const SkColor kDisabledMenuItemForegroundColor = kTextButtonDisabledColor;
+const SkColor kFocusedMenuItemBackgroundColor = SkColorSetRGB(246, 249, 253);
+const SkColor kMenuSeparatorColor = SkColorSetARGB(50, 0, 0, 0);
+// Label:
+const SkColor kLabelEnabledColor = color_utils::GetSysSkColor(COLOR_WINDOWTEXT);
+const SkColor kLabelDisabledColor = color_utils::GetSysSkColor(COLOR_GRAYTEXT);
+const SkColor kLabelBackgroundColor = color_utils::GetSysSkColor(COLOR_WINDOW);
+// Textfield:
+const SkColor kTextfieldDefaultColor = SK_ColorBLACK;
+const SkColor kTextfieldDefaultBackground = SK_ColorWHITE;
+const SkColor kTextfieldSelectionColor = SK_ColorWHITE;
+const SkColor kTextfieldSelectionBackgroundFocused =
+ SkColorSetRGB(0x1D, 0x90, 0xFF);
+const SkColor kTextfieldSelectionBackgroundUnfocused = SK_ColorLTGRAY;
+
+SkColor WinColorToSkColor(COLORREF color) {
+ return SkColorSetRGB(GetRValue(color), GetGValue(color), GetBValue(color));
+}
+
+void SetCheckerboardShader(SkPaint* paint, const RECT& align_rect) {
+ // Create a 2x2 checkerboard pattern using the 3D face and highlight colors.
+ SkColor face = skia::COLORREFToSkColor(GetSysColor(COLOR_3DFACE));
+ SkColor highlight = skia::COLORREFToSkColor(GetSysColor(COLOR_3DHILIGHT));
+ SkColor buffer[] = { face, highlight, highlight, face };
+ // Confusing bit: we first create a temporary bitmap with our desired pattern,
+ // then copy it to another bitmap. The temporary bitmap doesn't take
+ // ownership of the pixel data, and so will point to garbage when this
+ // function returns. The copy will copy the pixel data into a place owned by
+ // the bitmap, which is in turn owned by the shader, etc., so it will live
+ // until we're done using it.
+ SkBitmap temp_bitmap;
+ temp_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
+ temp_bitmap.setPixels(buffer);
+ SkBitmap bitmap;
+ temp_bitmap.copyTo(&bitmap, temp_bitmap.config());
+ SkShader* shader = SkShader::CreateBitmapShader(bitmap,
+ SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode);
+
+ // Align the pattern with the upper corner of |align_rect|.
+ SkMatrix matrix;
+ matrix.setTranslate(SkIntToScalar(align_rect.left),
+ SkIntToScalar(align_rect.top));
+ shader->setLocalMatrix(matrix);
+ SkSafeUnref(paint->setShader(shader));
+}
+
+// <-a->
+// [ ***** ]
+// ____ | |
+// <-a-> <------b----->
+// a: object_width
+// b: frame_width
+// *: animating object
+//
+// - the animation goes from "[" to "]" repeatedly.
+// - the animation offset is at first "|"
+//
+int ComputeAnimationProgress(int frame_width,
+ int object_width,
+ int pixels_per_second,
+ double animated_seconds) {
+ int animation_width = frame_width + object_width;
+ double interval = static_cast<double>(animation_width) / pixels_per_second;
+ double ratio = fmod(animated_seconds, interval) / interval;
+ return static_cast<int>(animation_width * ratio) - object_width;
+}
+
+RECT InsetRect(const RECT* rect, int size) {
+ gfx::Rect result(*rect);
+ result.Inset(size, size);
+ return result.ToRECT();
+}
+
+} // namespace
+
+namespace ui {
+
+bool NativeThemeWin::IsThemingActive() const {
+ if (is_theme_active_)
+ return !!is_theme_active_();
+ return false;
+}
+
+HRESULT NativeThemeWin::GetThemeColor(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ SkColor* color) const {
+ HANDLE handle = GetThemeHandle(theme);
+ if (handle && get_theme_color_) {
+ COLORREF color_ref;
+ if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) ==
+ S_OK) {
+ *color = skia::COLORREFToSkColor(color_ref);
+ return S_OK;
+ }
+ }
+ return E_NOTIMPL;
+}
+
+SkColor NativeThemeWin::GetThemeColorWithDefault(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int default_sys_color) const {
+ SkColor color;
+ if (GetThemeColor(theme, part_id, state_id, prop_id, &color) != S_OK)
+ color = skia::COLORREFToSkColor(GetSysColor(default_sys_color));
+ return color;
+}
+
+gfx::Size NativeThemeWin::GetThemeBorderSize(ThemeName theme) const {
+ // For simplicity use the wildcard state==0, part==0, since it works
+ // for the cases we currently depend on.
+ int border;
+ if (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK)
+ return gfx::Size(border, border);
+ else
+ return gfx::Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE));
+}
+
+void NativeThemeWin::DisableTheming() const {
+ if (!set_theme_properties_)
+ return;
+ set_theme_properties_(0);
+}
+
+void NativeThemeWin::CloseHandles() const {
+ if (!close_theme_)
+ return;
+
+ for (int i = 0; i < LAST; ++i) {
+ if (theme_handles_[i]) {
+ close_theme_(theme_handles_[i]);
+ theme_handles_[i] = NULL;
+ }
+ }
+}
+
+bool NativeThemeWin::IsClassicTheme(ThemeName name) const {
+ if (!theme_dll_)
+ return true;
+
+ return !GetThemeHandle(name);
+}
+
+// TODO(sky): seems like we should default to NativeThemeWin, but that currently
+// breaks a couple of tests (FocusTraversalTest.NormalTraversal in
+// views_unittests).
+#if !defined(USE_AURA)
+// static
+NativeTheme* NativeTheme::instance() {
+ return NativeThemeWin::instance();
+}
+#endif
+
+// static
+NativeThemeWin* NativeThemeWin::instance() {
+ CR_DEFINE_STATIC_LOCAL(NativeThemeWin, s_native_theme, ());
+ return &s_native_theme;
+}
+
+gfx::Size NativeThemeWin::GetPartSize(Part part,
+ State state,
+ const ExtraParams& extra) const {
+ SIZE size;
+ int part_id = GetWindowsPart(part, state, extra);
+ int state_id = GetWindowsState(part, state, extra);
+
+ HDC hdc = GetDC(NULL);
+ HRESULT hr = GetThemePartSize(GetThemeName(part), hdc, part_id, state_id,
+ NULL, TS_TRUE, &size);
+ ReleaseDC(NULL, hdc);
+
+ if (FAILED(hr)) {
+ // TODO(rogerta): For now, we need to support radio buttons and checkboxes
+ // when theming is not enabled. Support for other parts can be added
+ // if/when needed.
+ switch (part) {
+ case kCheckbox:
+ case kRadio:
+ // TODO(rogerta): I was not able to find any API to get the default
+ // size of these controls, so determined these values empirically.
+ size.cx = 13;
+ size.cy = 13;
+ break;
+ default:
+ size.cx = 0;
+ size.cy = 0;
+ break;
+ }
+ }
+
+ return gfx::Size(size.cx, size.cy);
+}
+
+void NativeThemeWin::Paint(SkCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ExtraParams& extra) const {
+ bool needs_paint_indirect = false;
+ if (!skia::SupportsPlatformPaint(canvas)) {
+ // This block will only get hit with --enable-accelerated-drawing flag.
+ needs_paint_indirect = true;
+ } else {
+ // Scrollbars on Windows XP and the Windows Classic theme have particularly
+ // problematic alpha values, so always draw them indirectly.
+ switch (part) {
+ case kScrollbarDownArrow:
+ case kScrollbarUpArrow:
+ case kScrollbarLeftArrow:
+ case kScrollbarRightArrow:
+ case kScrollbarHorizontalThumb:
+ case kScrollbarVerticalThumb:
+ case kScrollbarHorizontalGripper:
+ case kScrollbarVerticalGripper:
+ if (!GetThemeHandle(SCROLLBAR))
+ needs_paint_indirect = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (needs_paint_indirect)
+ PaintIndirect(canvas, part, state, rect, extra);
+ else
+ PaintDirect(canvas, part, state, rect, extra);
+}
+
+NativeThemeWin::NativeThemeWin()
+ : theme_dll_(LoadLibrary(L"uxtheme.dll")),
+ draw_theme_(NULL),
+ draw_theme_ex_(NULL),
+ get_theme_color_(NULL),
+ get_theme_content_rect_(NULL),
+ get_theme_part_size_(NULL),
+ open_theme_(NULL),
+ close_theme_(NULL),
+ set_theme_properties_(NULL),
+ is_theme_active_(NULL),
+ get_theme_int_(NULL) {
+ if (theme_dll_) {
+ draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>(
+ GetProcAddress(theme_dll_, "DrawThemeBackground"));
+ draw_theme_ex_ = reinterpret_cast<DrawThemeBackgroundExPtr>(
+ GetProcAddress(theme_dll_, "DrawThemeBackgroundEx"));
+ get_theme_color_ = reinterpret_cast<GetThemeColorPtr>(
+ GetProcAddress(theme_dll_, "GetThemeColor"));
+ get_theme_content_rect_ = reinterpret_cast<GetThemeContentRectPtr>(
+ GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect"));
+ get_theme_part_size_ = reinterpret_cast<GetThemePartSizePtr>(
+ GetProcAddress(theme_dll_, "GetThemePartSize"));
+ open_theme_ = reinterpret_cast<OpenThemeDataPtr>(
+ GetProcAddress(theme_dll_, "OpenThemeData"));
+ close_theme_ = reinterpret_cast<CloseThemeDataPtr>(
+ GetProcAddress(theme_dll_, "CloseThemeData"));
+ set_theme_properties_ = reinterpret_cast<SetThemeAppPropertiesPtr>(
+ GetProcAddress(theme_dll_, "SetThemeAppProperties"));
+ is_theme_active_ = reinterpret_cast<IsThemeActivePtr>(
+ GetProcAddress(theme_dll_, "IsThemeActive"));
+ get_theme_int_ = reinterpret_cast<GetThemeIntPtr>(
+ GetProcAddress(theme_dll_, "GetThemeInt"));
+ }
+ memset(theme_handles_, 0, sizeof(theme_handles_));
+}
+
+NativeThemeWin::~NativeThemeWin() {
+ if (theme_dll_) {
+ // todo (cpu): fix this soon. Making a call to CloseHandles() here breaks
+ // certain tests and the reliability bots.
+ // CloseHandles();
+ FreeLibrary(theme_dll_);
+ }
+}
+
+void NativeThemeWin::PaintDirect(SkCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ExtraParams& extra) const {
+ skia::ScopedPlatformPaint scoped_platform_paint(canvas);
+ HDC hdc = scoped_platform_paint.GetPlatformSurface();
+
+ switch (part) {
+ case kCheckbox:
+ PaintCheckbox(hdc, part, state, rect, extra.button);
+ break;
+ case kRadio:
+ PaintRadioButton(hdc, part, state, rect, extra.button);
+ break;
+ case kPushButton:
+ PaintPushButton(hdc, part, state, rect, extra.button);
+ break;
+ case kMenuPopupArrow:
+ PaintMenuArrow(hdc, state, rect, extra.menu_arrow);
+ break;
+ case kMenuPopupGutter:
+ PaintMenuGutter(hdc, rect);
+ break;
+ case kMenuPopupSeparator:
+ PaintMenuSeparator(hdc, rect, extra.menu_separator);
+ break;
+ case kMenuPopupBackground:
+ PaintMenuBackground(hdc, rect);
+ break;
+ case kMenuCheck:
+ PaintMenuCheck(hdc, state, rect, extra.menu_check);
+ break;
+ case kMenuCheckBackground:
+ PaintMenuCheckBackground(hdc, state, rect);
+ break;
+ case kMenuItemBackground:
+ PaintMenuItemBackground(hdc, state, rect, extra.menu_item);
+ break;
+ case kMenuList:
+ PaintMenuList(hdc, state, rect, extra.menu_list);
+ break;
+ case kScrollbarDownArrow:
+ case kScrollbarUpArrow:
+ case kScrollbarLeftArrow:
+ case kScrollbarRightArrow:
+ PaintScrollbarArrow(hdc, part, state, rect, extra.scrollbar_arrow);
+ break;
+ case kScrollbarHorizontalTrack:
+ case kScrollbarVerticalTrack:
+ PaintScrollbarTrack(canvas, hdc, part, state, rect,
+ extra.scrollbar_track);
+ break;
+ case kScrollbarHorizontalThumb:
+ case kScrollbarVerticalThumb:
+ case kScrollbarHorizontalGripper:
+ case kScrollbarVerticalGripper:
+ PaintScrollbarThumb(hdc, part, state, rect, extra.scrollbar_thumb);
+ break;
+ case kInnerSpinButton:
+ PaintSpinButton(hdc, part, state, rect, extra.inner_spin);
+ break;
+ case kTrackbarThumb:
+ case kTrackbarTrack:
+ PaintTrackbar(canvas, hdc, part, state, rect, extra.trackbar);
+ break;
+ case kProgressBar:
+ PaintProgressBar(hdc, rect, extra.progress_bar);
+ break;
+ case kWindowResizeGripper:
+ PaintWindowResizeGripper(hdc, rect);
+ break;
+ case kTabPanelBackground:
+ PaintTabPanelBackground(hdc, rect);
+ break;
+ case kTextField:
+ PaintTextField(hdc, part, state, rect, extra.text_field);
+ break;
+
+ case kSliderTrack:
+ case kSliderThumb:
+ default:
+ // While transitioning NativeThemeWin to the single Paint() entry point,
+ // unsupported parts will DCHECK here.
+ NOTREACHED();
+ }
+}
+
+SkColor NativeThemeWin::GetSystemColor(ColorId color_id) const {
+ switch (color_id) {
+
+ // Dialogs
+ case kColorId_DialogBackground:
+ // TODO(benrg): Should this use the new Windows theme functions? The old
+ // code in DialogClientView::OnPaint used GetSysColor(COLOR_3DFACE).
+ return WinColorToSkColor(GetSysColor(COLOR_3DFACE));
+
+ // FocusableBorder
+ case kColorId_FocusedBorderColor:
+ return kFocusedBorderColor;
+ case kColorId_UnfocusedBorderColor:
+ return kUnfocusedBorderColor;
+
+ // TextButton
+ case kColorId_TextButtonBackgroundColor:
+ return kTextButtonBackgroundColor;
+ case kColorId_TextButtonEnabledColor:
+ return kTextButtonEnabledColor;
+ case kColorId_TextButtonDisabledColor:
+ return kTextButtonDisabledColor;
+ case kColorId_TextButtonHighlightColor:
+ return kTextButtonHighlightColor;
+ case kColorId_TextButtonHoverColor:
+ return kTextButtonHoverColor;
+
+ // MenuItem
+ case kColorId_EnabledMenuItemForegroundColor:
+ return kEnabledMenuItemForegroundColor;
+ case kColorId_DisabledMenuItemForegroundColor:
+ return kDisabledMenuItemForegroundColor;
+ case kColorId_FocusedMenuItemBackgroundColor:
+ return kFocusedMenuItemBackgroundColor;
+ case kColorId_MenuSeparatorColor:
+ return kMenuSeparatorColor;
+
+ // Label
+ case kColorId_LabelEnabledColor:
+ return kLabelEnabledColor;
+ case kColorId_LabelDisabledColor:
+ return kLabelDisabledColor;
+ case kColorId_LabelBackgroundColor:
+ return kLabelBackgroundColor;
+
+ // Textfield
+ case kColorId_TextfieldDefaultColor:
+ return kTextfieldDefaultColor;
+ case kColorId_TextfieldDefaultBackground:
+ return kTextfieldDefaultBackground;
+ case kColorId_TextfieldSelectionColor:
+ return kTextfieldSelectionColor;
+ case kColorId_TextfieldSelectionBackgroundFocused:
+ return kTextfieldSelectionBackgroundFocused;
+ case kColorId_TextfieldSelectionBackgroundUnfocused:
+ return kTextfieldSelectionBackgroundUnfocused;
+
+ default:
+ NOTREACHED() << "Invalid color_id: " << color_id;
+ break;
+ }
+ return kInvalidColorIdColor;
+}
+
+void NativeThemeWin::PaintIndirect(SkCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ExtraParams& extra) const {
+ // TODO(asvitkine): This path is pretty inefficient - for each paint operation
+ // it creates a new offscreen bitmap Skia canvas. This can
+ // be sped up by doing it only once per part/state and
+ // keeping a cache of the resulting bitmaps.
+
+ // Create an offscreen canvas that is backed by an HDC.
+ skia::BitmapPlatformDevice* device = skia::BitmapPlatformDevice::Create(
+ rect.width(), rect.height(), false, NULL);
+ DCHECK(device);
+ SkCanvas offscreen_canvas(device);
+ device->unref();
+ DCHECK(skia::SupportsPlatformPaint(&offscreen_canvas));
+
+ // Some of the Windows theme drawing operations do not write correct alpha
+ // values for fully-opaque pixels; instead the pixels get alpha 0. This is
+ // especially a problem on Windows XP or when using the Classic theme.
+ //
+ // To work-around this, mark all pixels with a placeholder value, to detect
+ // which pixels get touched by the paint operation. After paint, set any
+ // pixels that have alpha 0 to opaque and placeholders to fully-transparent.
+ const SkColor placeholder = SkColorSetARGB(1, 0, 0, 0);
+ offscreen_canvas.clear(placeholder);
+
+ // Offset destination rects to have origin (0,0).
+ gfx::Rect adjusted_rect(rect.size());
+ ExtraParams adjusted_extra(extra);
+ switch (part) {
+ case kProgressBar:
+ adjusted_extra.progress_bar.value_rect_x = 0;
+ adjusted_extra.progress_bar.value_rect_y = 0;
+ break;
+ case kScrollbarHorizontalTrack:
+ case kScrollbarVerticalTrack:
+ adjusted_extra.scrollbar_track.track_x = 0;
+ adjusted_extra.scrollbar_track.track_y = 0;
+ break;
+ default: break;
+ }
+ // Draw the theme controls using existing HDC-drawing code.
+ PaintDirect(&offscreen_canvas,
+ part,
+ state,
+ adjusted_rect,
+ adjusted_extra);
+
+ // Copy the pixels to a bitmap that has ref-counted pixel storage, which is
+ // necessary to have when drawing to a SkPicture.
+ const SkBitmap& hdc_bitmap =
+ offscreen_canvas.getDevice()->accessBitmap(false);
+ SkBitmap bitmap;
+ hdc_bitmap.copyTo(&bitmap, SkBitmap::kARGB_8888_Config);
+
+ // Post-process the pixels to fix up the alpha values (see big comment above).
+ const SkPMColor placeholder_value = SkPreMultiplyColor(placeholder);
+ const int pixel_count = rect.width() * rect.height();
+ SkPMColor* pixels = bitmap.getAddr32(0, 0);
+ for (int i = 0; i < pixel_count; i++) {
+ if (pixels[i] == placeholder_value) {
+ // Pixel wasn't touched - make it fully transparent.
+ pixels[i] = SkPackARGB32(0, 0, 0, 0);
+ } else if (SkGetPackedA32(pixels[i]) == 0) {
+ // Pixel was touched but has incorrect alpha of 0, make it fully opaque.
+ pixels[i] = SkPackARGB32(0xFF,
+ SkGetPackedR32(pixels[i]),
+ SkGetPackedG32(pixels[i]),
+ SkGetPackedB32(pixels[i]));
+ }
+ }
+
+ // Draw the offscreen bitmap to the destination canvas.
+ canvas->drawBitmap(bitmap, rect.x(), rect.y());
+}
+
+HRESULT NativeThemeWin::GetThemePartSize(ThemeName theme_name,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ int ts,
+ SIZE* size) const {
+ HANDLE handle = GetThemeHandle(theme_name);
+ if (handle && get_theme_part_size_)
+ return get_theme_part_size_(handle, hdc, part_id, state_id, rect, ts, size);
+
+ return E_NOTIMPL;
+}
+
+HRESULT NativeThemeWin::PaintButton(HDC hdc,
+ State state,
+ const ButtonExtraParams& extra,
+ int part_id,
+ int state_id,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(BUTTON);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+
+ // Adjust classic_state based on part, state, and extras.
+ int classic_state = extra.classic_state;
+ switch (part_id) {
+ case BP_CHECKBOX:
+ classic_state |= DFCS_BUTTONCHECK;
+ break;
+ case BP_RADIOBUTTON:
+ classic_state |= DFCS_BUTTONRADIO;
+ break;
+ case BP_PUSHBUTTON:
+ classic_state |= DFCS_BUTTONPUSH;
+ break;
+ default:
+ NOTREACHED() << "Unknown part_id: " << part_id;
+ break;
+ }
+
+ switch (state) {
+ case kDisabled:
+ classic_state |= DFCS_INACTIVE;
+ break;
+ case kPressed:
+ classic_state |= DFCS_PUSHED;
+ break;
+ case kNormal:
+ case kHovered:
+ break;
+ default:
+ NOTREACHED() << "Unknown state: " << state;
+ break;
+ }
+
+ if (extra.checked)
+ classic_state |= DFCS_CHECKED;
+
+ // Draw it manually.
+ // All pressed states have both low bits set, and no other states do.
+ const bool focused = ((state_id & ETS_FOCUSED) == ETS_FOCUSED);
+ const bool pressed = ((state_id & PBS_PRESSED) == PBS_PRESSED);
+ if ((BP_PUSHBUTTON == part_id) && (pressed || focused)) {
+ // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the
+ // button itself is shrunk by 1 pixel.
+ HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW);
+ if (brush) {
+ FrameRect(hdc, rect, brush);
+ InflateRect(rect, -1, -1);
+ }
+ }
+ DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state);
+
+ // Draw the focus rectangle (the dotted line box) only on buttons. For radio
+ // and checkboxes, we let webkit draw the focus rectangle (orange glow).
+ if ((BP_PUSHBUTTON == part_id) && focused) {
+ // The focus rect is inside the button. The exact number of pixels depends
+ // on whether we're in classic mode or using uxtheme.
+ if (handle && get_theme_content_rect_) {
+ get_theme_content_rect_(handle, hdc, part_id, state_id, rect, rect);
+ } else {
+ InflateRect(rect, -GetSystemMetrics(SM_CXEDGE),
+ -GetSystemMetrics(SM_CYEDGE));
+ }
+ DrawFocusRect(hdc, rect);
+ }
+
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintMenuSeparator(
+ HDC hdc,
+ const gfx::Rect& rect,
+ const MenuSeparatorExtraParams& extra) const {
+ RECT rect_win = rect.ToRECT();
+
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_) {
+ // Delta is needed for non-classic to move separator up slightly.
+ --rect_win.top;
+ --rect_win.bottom;
+ return draw_theme_(handle, hdc, MENU_POPUPSEPARATOR, MPI_NORMAL, &rect_win,
+ NULL);
+ }
+
+ DrawEdge(hdc, &rect_win, EDGE_ETCHED, BF_TOP);
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintMenuGutter(HDC hdc,
+ const gfx::Rect& rect) const {
+ RECT rect_win = rect.ToRECT();
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, MENU_POPUPGUTTER, MPI_NORMAL, &rect_win,
+ NULL);
+ return E_NOTIMPL;
+}
+
+HRESULT NativeThemeWin::PaintMenuArrow(HDC hdc,
+ State state,
+ const gfx::Rect& rect,
+ const MenuArrowExtraParams& extra)
+ const {
+ int state_id = MSM_NORMAL;
+ if (state == kDisabled)
+ state_id = MSM_DISABLED;
+
+ HANDLE handle = GetThemeHandle(MENU);
+ RECT rect_win = rect.ToRECT();
+ if (handle && draw_theme_) {
+ if (extra.pointing_right) {
+ return draw_theme_(handle, hdc, MENU_POPUPSUBMENU, state_id, &rect_win,
+ NULL);
+ } else {
+ // There is no way to tell the uxtheme API to draw a left pointing arrow;
+ // it doesn't have a flag equivalent to DFCS_MENUARROWRIGHT. But they
+ // are needed for RTL locales on Vista. So use a memory DC and mirror
+ // the region with GDI's StretchBlt.
+ gfx::Rect r(rect);
+ base::win::ScopedCreateDC mem_dc(CreateCompatibleDC(hdc));
+ base::win::ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(),
+ r.height()));
+ base::win::ScopedSelectObject select_bitmap(mem_dc, mem_bitmap);
+ // Copy and horizontally mirror the background from hdc into mem_dc. Use
+ // a negative-width source rect, starting at the rightmost pixel.
+ StretchBlt(mem_dc, 0, 0, r.width(), r.height(),
+ hdc, r.right()-1, r.y(), -r.width(), r.height(), SRCCOPY);
+ // Draw the arrow.
+ RECT theme_rect = {0, 0, r.width(), r.height()};
+ HRESULT result = draw_theme_(handle, mem_dc, MENU_POPUPSUBMENU,
+ state_id, &theme_rect, NULL);
+ // Copy and mirror the result back into mem_dc.
+ StretchBlt(hdc, r.x(), r.y(), r.width(), r.height(),
+ mem_dc, r.width()-1, 0, -r.width(), r.height(), SRCCOPY);
+ return result;
+ }
+ }
+
+ // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
+ // left pointing arrow. This makes the following 'if' statement slightly
+ // counterintuitive.
+ UINT pfc_state;
+ if (extra.pointing_right)
+ pfc_state = DFCS_MENUARROW;
+ else
+ pfc_state = DFCS_MENUARROWRIGHT;
+ return PaintFrameControl(hdc, rect, DFC_MENU, pfc_state, extra.is_selected,
+ state);
+}
+
+HRESULT NativeThemeWin::PaintMenuBackground(HDC hdc,
+ const gfx::Rect& rect) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ RECT rect_win = rect.ToRECT();
+ if (handle && draw_theme_) {
+ HRESULT result = draw_theme_(handle, hdc, MENU_POPUPBACKGROUND, 0,
+ &rect_win, NULL);
+ FrameRect(hdc, &rect_win, GetSysColorBrush(COLOR_3DSHADOW));
+ return result;
+ }
+
+ FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_MENU));
+ DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT);
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintMenuCheck(
+ HDC hdc,
+ State state,
+ const gfx::Rect& rect,
+ const MenuCheckExtraParams& extra) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ int state_id;
+ if (extra.is_radio) {
+ state_id = state == kDisabled ? MC_BULLETDISABLED : MC_BULLETNORMAL;
+ } else {
+ state_id = state == kDisabled ? MC_CHECKMARKDISABLED : MC_CHECKMARKNORMAL;
+ }
+
+ RECT rect_win = rect.ToRECT();
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, MENU_POPUPCHECK, state_id, &rect_win, NULL);
+
+ return PaintFrameControl(hdc, rect, DFC_MENU,
+ extra.is_radio ? DFCS_MENUBULLET : DFCS_MENUCHECK,
+ extra.is_selected, state);
+}
+
+HRESULT NativeThemeWin::PaintMenuCheckBackground(HDC hdc,
+ State state,
+ const gfx::Rect& rect) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ int state_id = state == kDisabled ? MCB_DISABLED : MCB_NORMAL;
+ RECT rect_win = rect.ToRECT();
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, MENU_POPUPCHECKBACKGROUND, state_id,
+ &rect_win, NULL);
+ // Nothing to do for background.
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintMenuItemBackground(
+ HDC hdc,
+ State state,
+ const gfx::Rect& rect,
+ const MenuItemExtraParams& extra) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ RECT rect_win = rect.ToRECT();
+ int state_id;
+ switch (state) {
+ case kNormal:
+ state_id = MPI_NORMAL;
+ break;
+ case kDisabled:
+ state_id = extra.is_selected ? MPI_DISABLEDHOT : MPI_DISABLED;
+ break;
+ case kHovered:
+ state_id = MPI_HOT;
+ break;
+ default:
+ NOTREACHED() << "Invalid state " << state;
+ break;
+ }
+
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, MENU_POPUPITEM, state_id, &rect_win, NULL);
+
+ if (extra.is_selected)
+ FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_HIGHLIGHT));
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintPushButton(HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& extra) const {
+ int state_id;
+ switch (state) {
+ case kDisabled:
+ state_id = PBS_DISABLED;
+ break;
+ case kHovered:
+ state_id = PBS_HOT;
+ break;
+ case kNormal:
+ state_id = extra.is_default ? PBS_DEFAULTED : PBS_NORMAL;
+ break;
+ case kPressed:
+ state_id = PBS_PRESSED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+
+ RECT rect_win = rect.ToRECT();
+ return PaintButton(hdc, state, extra, BP_PUSHBUTTON, state_id, &rect_win);
+}
+
+HRESULT NativeThemeWin::PaintRadioButton(HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& extra) const {
+ int state_id;
+ switch (state) {
+ case kDisabled:
+ state_id = extra.checked ? RBS_CHECKEDDISABLED : RBS_UNCHECKEDDISABLED;
+ break;
+ case kHovered:
+ state_id = extra.checked ? RBS_CHECKEDHOT : RBS_UNCHECKEDHOT;
+ break;
+ case kNormal:
+ state_id = extra.checked ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL;
+ break;
+ case kPressed:
+ state_id = extra.checked ? RBS_CHECKEDPRESSED : RBS_UNCHECKEDPRESSED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+
+ RECT rect_win = rect.ToRECT();
+ return PaintButton(hdc, state, extra, BP_RADIOBUTTON, state_id, &rect_win);
+}
+
+HRESULT NativeThemeWin::PaintCheckbox(HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& extra) const {
+ int state_id;
+ switch (state) {
+ case kDisabled:
+ state_id = extra.checked ? CBS_CHECKEDDISABLED :
+ extra.indeterminate ? CBS_MIXEDDISABLED :
+ CBS_UNCHECKEDDISABLED;
+ break;
+ case kHovered:
+ state_id = extra.checked ? CBS_CHECKEDHOT :
+ extra.indeterminate ? CBS_MIXEDHOT :
+ CBS_UNCHECKEDHOT;
+ break;
+ case kNormal:
+ state_id = extra.checked ? CBS_CHECKEDNORMAL :
+ extra.indeterminate ? CBS_MIXEDNORMAL :
+ CBS_UNCHECKEDNORMAL;
+ break;
+ case kPressed:
+ state_id = extra.checked ? CBS_CHECKEDPRESSED :
+ extra.indeterminate ? CBS_MIXEDPRESSED :
+ CBS_UNCHECKEDPRESSED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+
+ RECT rect_win = rect.ToRECT();
+ return PaintButton(hdc, state, extra, BP_CHECKBOX, state_id, &rect_win);
+}
+
+HRESULT NativeThemeWin::PaintMenuList(HDC hdc,
+ State state,
+ const gfx::Rect& rect,
+ const MenuListExtraParams& extra) const {
+ HANDLE handle = GetThemeHandle(MENULIST);
+ RECT rect_win = rect.ToRECT();
+ int state_id;
+ switch (state) {
+ case kNormal:
+ state_id = CBXS_NORMAL;
+ break;
+ case kDisabled:
+ state_id = CBXS_DISABLED;
+ break;
+ case kHovered:
+ state_id = CBXS_HOT;
+ break;
+ case kPressed:
+ state_id = CBXS_PRESSED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state " << state;
+ break;
+ }
+
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, CP_DROPDOWNBUTTON, state_id, &rect_win,
+ NULL);
+
+ // Draw it manually.
+ DrawFrameControl(hdc, &rect_win, DFC_SCROLL,
+ DFCS_SCROLLCOMBOBOX | extra.classic_state);
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintScrollbarArrow(
+ HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ScrollbarArrowExtraParams& extra) const {
+ static const int state_id_matrix[4][kMaxState] = {
+ ABS_DOWNDISABLED, ABS_DOWNHOT, ABS_DOWNNORMAL, ABS_DOWNPRESSED,
+ ABS_LEFTDISABLED, ABS_LEFTHOT, ABS_LEFTNORMAL, ABS_LEFTPRESSED,
+ ABS_RIGHTDISABLED, ABS_RIGHTHOT, ABS_RIGHTNORMAL, ABS_RIGHTPRESSED,
+ ABS_UPDISABLED, ABS_UPHOT, ABS_UPNORMAL, ABS_UPPRESSED
+ };
+ HANDLE handle = GetThemeHandle(SCROLLBAR);
+ RECT rect_win = rect.ToRECT();
+ if (handle && draw_theme_) {
+ int index = part - kScrollbarDownArrow;
+ DCHECK(index >=0 && index < 4);
+ int state_id = state_id_matrix[index][state];
+
+ // Hovering means that the cursor is over the scroolbar, but not over the
+ // specific arrow itself. We don't want to show it "hot" mode, but only
+ // in "hover" mode.
+ if (state == kHovered && extra.is_hovering) {
+ switch (part) {
+ case kScrollbarDownArrow:
+ state_id = ABS_DOWNHOVER;
+ break;
+ case kScrollbarLeftArrow:
+ state_id = ABS_LEFTHOVER;
+ break;
+ case kScrollbarRightArrow:
+ state_id = ABS_RIGHTHOVER;
+ break;
+ case kScrollbarUpArrow:
+ state_id = ABS_UPHOVER;
+ break;
+ default:
+ NOTREACHED() << "Invalid part: " << part;
+ break;
+ }
+ }
+
+ return draw_theme_(handle, hdc, SBP_ARROWBTN, state_id, &rect_win, NULL);
+ }
+
+ int classic_state = DFCS_SCROLLDOWN;
+ switch (part) {
+ case kScrollbarDownArrow:
+ classic_state = DFCS_SCROLLDOWN;
+ break;
+ case kScrollbarLeftArrow:
+ classic_state = DFCS_SCROLLLEFT;
+ break;
+ case kScrollbarRightArrow:
+ classic_state = DFCS_SCROLLRIGHT;
+ break;
+ case kScrollbarUpArrow:
+ classic_state = DFCS_SCROLLUP;
+ break;
+ default:
+ NOTREACHED() << "Invalid part: " << part;
+ break;
+ }
+ switch (state) {
+ case kDisabled:
+ classic_state |= DFCS_INACTIVE;
+ break;
+ case kHovered:
+ classic_state |= DFCS_HOT;
+ break;
+ case kNormal:
+ break;
+ case kPressed:
+ classic_state |= DFCS_PUSHED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ DrawFrameControl(hdc, &rect_win, DFC_SCROLL, classic_state);
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintScrollbarThumb(
+ HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ScrollbarThumbExtraParams& extra) const {
+ HANDLE handle = GetThemeHandle(SCROLLBAR);
+ RECT rect_win = rect.ToRECT();
+ int part_id;
+ int state_id;
+
+ switch (part) {
+ case NativeTheme::kScrollbarHorizontalThumb:
+ part_id = SBP_THUMBBTNHORZ;
+ break;
+ case NativeTheme::kScrollbarVerticalThumb:
+ part_id = SBP_THUMBBTNVERT;
+ break;
+ case NativeTheme::kScrollbarHorizontalGripper:
+ part_id = SBP_GRIPPERHORZ;
+ break;
+ case NativeTheme::kScrollbarVerticalGripper:
+ part_id = SBP_GRIPPERVERT;
+ break;
+ default:
+ NOTREACHED() << "Invalid part: " << part;
+ break;
+ }
+
+ switch (state) {
+ case kDisabled:
+ state_id = SCRBS_DISABLED;
+ break;
+ case kHovered:
+ state_id = extra.is_hovering ? SCRBS_HOVER : SCRBS_HOT;
+ break;
+ case kNormal:
+ state_id = SCRBS_NORMAL;
+ break;
+ case kPressed:
+ state_id = SCRBS_PRESSED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
+
+ // Draw it manually.
+ if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT))
+ DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_MIDDLE);
+ // Classic mode doesn't have a gripper.
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintScrollbarTrack(
+ SkCanvas* canvas,
+ HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ScrollbarTrackExtraParams& extra) const {
+ HANDLE handle = GetThemeHandle(SCROLLBAR);
+ RECT rect_win = rect.ToRECT();
+ int part_id;
+ int state_id;
+
+ switch (part) {
+ case NativeTheme::kScrollbarHorizontalTrack:
+ part_id = extra.is_upper ? SBP_UPPERTRACKHORZ : SBP_LOWERTRACKHORZ;
+ break;
+ case NativeTheme::kScrollbarVerticalTrack:
+ part_id = extra.is_upper ? SBP_UPPERTRACKVERT : SBP_LOWERTRACKVERT;
+ break;
+ default:
+ NOTREACHED() << "Invalid part: " << part;
+ break;
+ }
+
+ switch (state) {
+ case kDisabled:
+ state_id = SCRBS_DISABLED;
+ break;
+ case kHovered:
+ state_id = SCRBS_HOVER;
+ break;
+ case kNormal:
+ state_id = SCRBS_NORMAL;
+ break;
+ case kPressed:
+ state_id = SCRBS_PRESSED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
+
+ // Draw it manually.
+ const DWORD colorScrollbar = GetSysColor(COLOR_SCROLLBAR);
+ const DWORD color3DFace = GetSysColor(COLOR_3DFACE);
+ if ((colorScrollbar != color3DFace) &&
+ (colorScrollbar != GetSysColor(COLOR_WINDOW))) {
+ FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_SCROLLBAR + 1));
+ } else {
+ SkPaint paint;
+ RECT align_rect = gfx::Rect(extra.track_x, extra.track_y, extra.track_width,
+ extra.track_height).ToRECT();
+ SetCheckerboardShader(&paint, align_rect);
+ canvas->drawIRect(skia::RECTToSkIRect(rect_win), paint);
+ }
+ if (extra.classic_state & DFCS_PUSHED)
+ InvertRect(hdc, &rect_win);
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintSpinButton(
+ HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const InnerSpinButtonExtraParams& extra) const {
+ HANDLE handle = GetThemeHandle(SPIN);
+ RECT rect_win = rect.ToRECT();
+ int part_id = extra.spin_up ? SPNP_UP : SPNP_DOWN;
+ int state_id;
+ switch (state) {
+ case kDisabled:
+ state_id = extra.spin_up ? UPS_DISABLED : DNS_DISABLED;
+ break;
+ case kHovered:
+ state_id = extra.spin_up ? UPS_HOT : DNS_HOT;
+ break;
+ case kNormal:
+ state_id = extra.spin_up ? UPS_NORMAL : DNS_NORMAL;
+ break;
+ case kPressed:
+ state_id = extra.spin_up ? UPS_PRESSED : DNS_PRESSED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state " << state;
+ break;
+ }
+
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
+ DrawFrameControl(hdc, &rect_win, DFC_SCROLL, extra.classic_state);
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintTrackbar(
+ SkCanvas* canvas,
+ HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const TrackbarExtraParams& extra) const {
+ int part_id = part == kTrackbarTrack ? TKP_TRACK : TKP_THUMBBOTTOM;
+ if (extra.vertical)
+ part_id = part == kTrackbarTrack ? TKP_TRACKVERT : TKP_THUMBVERT;
+
+ int state_id = 0;
+ switch (state) {
+ case kDisabled:
+ state_id = TUS_DISABLED;
+ break;
+ case kHovered:
+ state_id = TUS_HOT;
+ break;
+ case kNormal:
+ state_id = TUS_NORMAL;
+ break;
+ case kPressed:
+ state_id = TUS_PRESSED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state " << state;
+ break;
+ }
+
+ // Make the channel be 4 px thick in the center of the supplied rect. (4 px
+ // matches what XP does in various menus; GetThemePartSize() doesn't seem to
+ // return good values here.)
+ RECT rect_win = rect.ToRECT();
+ RECT channel_rect = rect.ToRECT();
+ const int channel_thickness = 4;
+ if (part_id == TKP_TRACK) {
+ channel_rect.top +=
+ ((channel_rect.bottom - channel_rect.top - channel_thickness) / 2);
+ channel_rect.bottom = channel_rect.top + channel_thickness;
+ } else if (part_id == TKP_TRACKVERT) {
+ channel_rect.left +=
+ ((channel_rect.right - channel_rect.left - channel_thickness) / 2);
+ channel_rect.right = channel_rect.left + channel_thickness;
+ } // else this isn't actually a channel, so |channel_rect| == |rect|.
+
+ HANDLE handle = GetThemeHandle(TRACKBAR);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, &channel_rect, NULL);
+
+ // Classic mode, draw it manually.
+ if ((part_id == TKP_TRACK) || (part_id == TKP_TRACKVERT)) {
+ DrawEdge(hdc, &channel_rect, EDGE_SUNKEN, BF_RECT);
+ } else if (part_id == TKP_THUMBVERT) {
+ DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE);
+ } else {
+ // Split rect into top and bottom pieces.
+ RECT top_section = rect.ToRECT();
+ RECT bottom_section = rect.ToRECT();
+ top_section.bottom -= ((bottom_section.right - bottom_section.left) / 2);
+ bottom_section.top = top_section.bottom;
+ DrawEdge(hdc, &top_section, EDGE_RAISED,
+ BF_LEFT | BF_TOP | BF_RIGHT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
+
+ // Split triangular piece into two diagonals.
+ RECT& left_half = bottom_section;
+ RECT right_half = bottom_section;
+ right_half.left += ((bottom_section.right - bottom_section.left) / 2);
+ left_half.right = right_half.left;
+ DrawEdge(hdc, &left_half, EDGE_RAISED,
+ BF_DIAGONAL_ENDTOPLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
+ DrawEdge(hdc, &right_half, EDGE_RAISED,
+ BF_DIAGONAL_ENDBOTTOMLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
+
+ // If the button is pressed, draw hatching.
+ if (extra.classic_state & DFCS_PUSHED) {
+ SkPaint paint;
+ SetCheckerboardShader(&paint, rect_win);
+
+ // Fill all three pieces with the pattern.
+ canvas->drawIRect(skia::RECTToSkIRect(top_section), paint);
+
+ SkScalar left_triangle_top = SkIntToScalar(left_half.top);
+ SkScalar left_triangle_right = SkIntToScalar(left_half.right);
+ SkPath left_triangle;
+ left_triangle.moveTo(SkIntToScalar(left_half.left), left_triangle_top);
+ left_triangle.lineTo(left_triangle_right, left_triangle_top);
+ left_triangle.lineTo(left_triangle_right,
+ SkIntToScalar(left_half.bottom));
+ left_triangle.close();
+ canvas->drawPath(left_triangle, paint);
+
+ SkScalar right_triangle_left = SkIntToScalar(right_half.left);
+ SkScalar right_triangle_top = SkIntToScalar(right_half.top);
+ SkPath right_triangle;
+ right_triangle.moveTo(right_triangle_left, right_triangle_top);
+ right_triangle.lineTo(SkIntToScalar(right_half.right),
+ right_triangle_top);
+ right_triangle.lineTo(right_triangle_left,
+ SkIntToScalar(right_half.bottom));
+ right_triangle.close();
+ canvas->drawPath(right_triangle, paint);
+ }
+ }
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintProgressBar(
+ HDC hdc,
+ const gfx::Rect& rect,
+ const ProgressBarExtraParams& extra) const {
+ // There is no documentation about the animation speed, frame-rate, nor
+ // size of moving overlay of the indeterminate progress bar.
+ // So we just observed real-world programs and guessed following parameters.
+ const int kDeteminateOverlayPixelsPerSecond = 300;
+ const int kDeteminateOverlayWidth = 120;
+ const int kIndeterminateOverlayPixelsPerSecond = 175;
+ const int kVistaIndeterminateOverlayWidth = 120;
+ const int kXPIndeterminateOverlayWidth = 55;
+ // The thickness of the bar frame inside |value_rect|
+ const int kXPBarPadding = 3;
+
+ RECT bar_rect = rect.ToRECT();
+ RECT value_rect = gfx::Rect(extra.value_rect_x,
+ extra.value_rect_y,
+ extra.value_rect_width,
+ extra.value_rect_height).ToRECT();
+
+ bool pre_vista = base::win::GetVersion() < base::win::VERSION_VISTA;
+ HANDLE handle = GetThemeHandle(PROGRESS);
+ if (handle && draw_theme_ && draw_theme_ex_) {
+ draw_theme_(handle, hdc, PP_BAR, 0, &bar_rect, NULL);
+
+ int bar_width = bar_rect.right - bar_rect.left;
+ if (extra.determinate) {
+ // TODO(morrita): this RTL guess can be wrong.
+ // We should pass the direction from WebKit side.
+ bool is_rtl = (bar_rect.right == value_rect.right &&
+ bar_rect.left != value_rect.left);
+ // We should care the direction here because PP_CNUNK painting
+ // is asymmetric.
+ DTBGOPTS value_draw_options;
+ value_draw_options.dwSize = sizeof(DTBGOPTS);
+ value_draw_options.dwFlags = is_rtl ? DTBG_MIRRORDC : 0;
+ value_draw_options.rcClip = bar_rect;
+
+ if (pre_vista) {
+ // On XP, progress bar is chunk-style and has no glossy effect.
+ // We need to shrink destination rect to fit the part inside the bar
+ // with an appropriate margin.
+ RECT shrunk_value_rect = InsetRect(&value_rect, kXPBarPadding);
+ draw_theme_ex_(handle, hdc, PP_CHUNK, 0,
+ &shrunk_value_rect, &value_draw_options);
+ } else {
+ // On Vista or later, the progress bar part has a
+ // single-block value part. It also has glossy effect.
+ // And the value part has exactly same height as the bar part
+ // so we don't need to shrink the rect.
+ draw_theme_ex_(handle, hdc, PP_FILL, 0,
+ &value_rect, &value_draw_options);
+
+ int dx = ComputeAnimationProgress(bar_width,
+ kDeteminateOverlayWidth,
+ kDeteminateOverlayPixelsPerSecond,
+ extra.animated_seconds);
+ RECT overlay_rect = value_rect;
+ overlay_rect.left += dx;
+ overlay_rect.right = overlay_rect.left + kDeteminateOverlayWidth;
+ draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &value_rect);
+ }
+ } else {
+ // A glossy overlay for indeterminate progress bar has small pause
+ // after each animation. We emulate this by adding an invisible margin
+ // the animation has to traverse.
+ int width_with_margin = bar_width + kIndeterminateOverlayPixelsPerSecond;
+ int overlay_width = pre_vista ?
+ kXPIndeterminateOverlayWidth : kVistaIndeterminateOverlayWidth;
+ int dx = ComputeAnimationProgress(width_with_margin,
+ overlay_width,
+ kIndeterminateOverlayPixelsPerSecond,
+ extra.animated_seconds);
+ RECT overlay_rect = bar_rect;
+ overlay_rect.left += dx;
+ overlay_rect.right = overlay_rect.left + overlay_width;
+ if (pre_vista) {
+ RECT shrunk_rect = InsetRect(&overlay_rect, kXPBarPadding);
+ RECT shrunk_bar_rect = InsetRect(&bar_rect, kXPBarPadding);
+ draw_theme_(handle, hdc, PP_CHUNK, 0, &shrunk_rect, &shrunk_bar_rect);
+ } else {
+ draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &bar_rect);
+ }
+ }
+
+ return S_OK;
+ }
+
+ HBRUSH bg_brush = GetSysColorBrush(COLOR_BTNFACE);
+ HBRUSH fg_brush = GetSysColorBrush(COLOR_BTNSHADOW);
+ FillRect(hdc, &bar_rect, bg_brush);
+ FillRect(hdc, &value_rect, fg_brush);
+ DrawEdge(hdc, &bar_rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintWindowResizeGripper(HDC hdc,
+ const gfx::Rect& rect) const {
+ HANDLE handle = GetThemeHandle(STATUS);
+ RECT rect_win = rect.ToRECT();
+ if (handle && draw_theme_) {
+ // Paint the status bar gripper. There doesn't seem to be a
+ // standard gripper in Windows for the space between
+ // scrollbars. This is pretty close, but it's supposed to be
+ // painted over a status bar.
+ return draw_theme_(handle, hdc, SP_GRIPPER, 0, &rect_win, NULL);
+ }
+
+ // Draw a windows classic scrollbar gripper.
+ DrawFrameControl(hdc, &rect_win, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintTabPanelBackground(HDC hdc,
+ const gfx::Rect& rect) const {
+ HANDLE handle = GetThemeHandle(TAB);
+ RECT rect_win = rect.ToRECT();
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, TABP_BODY, 0, &rect_win, NULL);
+
+ // Classic just renders a flat color background.
+ FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintTextField(
+ HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const TextFieldExtraParams& extra) const {
+ int part_id = EP_EDITTEXT;
+ int state_id = ETS_NORMAL;
+ switch (state) {
+ case kNormal:
+ if (extra.is_read_only) {
+ state_id = ETS_READONLY;
+ } else if (extra.is_focused) {
+ state_id = ETS_FOCUSED;
+ } else {
+ state_id = ETS_NORMAL;
+ }
+ break;
+ case kHovered:
+ state_id = ETS_HOT;
+ break;
+ case kPressed:
+ state_id = ETS_SELECTED;
+ break;
+ case kDisabled:
+ state_id = ETS_DISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+
+ RECT rect_win = rect.ToRECT();
+ return PaintTextField(hdc, part_id, state_id, extra.classic_state,
+ &rect_win,
+ skia::SkColorToCOLORREF(extra.background_color),
+ extra.fill_content_area, extra.draw_edges);
+}
+
+HRESULT NativeThemeWin::PaintTextField(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect,
+ COLORREF color,
+ bool fill_content_area,
+ bool draw_edges) const {
+ // TODO(ojan): http://b/1210017 Figure out how to give the ability to
+ // exclude individual edges from being drawn.
+
+ HANDLE handle = GetThemeHandle(TEXTFIELD);
+ // TODO(mpcomplete): can we detect if the color is specified by the user,
+ // and if not, just use the system color?
+ // CreateSolidBrush() accepts a RGB value but alpha must be 0.
+ HBRUSH bg_brush = CreateSolidBrush(color);
+ HRESULT hr;
+ // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible
+ // draw_theme_ex_ is NULL and draw_theme_ is non-null.
+ if (handle && (draw_theme_ex_ || (draw_theme_ && draw_edges))) {
+ if (draw_theme_ex_) {
+ static const DTBGOPTS omit_border_options = {
+ sizeof(DTBGOPTS),
+ DTBG_OMITBORDER,
+ { 0, 0, 0, 0 }
+ };
+ const DTBGOPTS* draw_opts = draw_edges ? NULL : &omit_border_options;
+ hr = draw_theme_ex_(handle, hdc, part_id, state_id, rect, draw_opts);
+ } else {
+ hr = draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ }
+
+ // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
+ if (fill_content_area && get_theme_content_rect_) {
+ RECT content_rect;
+ hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect,
+ &content_rect);
+ FillRect(hdc, &content_rect, bg_brush);
+ }
+ } else {
+ // Draw it manually.
+ if (draw_edges)
+ DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+
+ if (fill_content_area) {
+ FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ?
+ reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1) : bg_brush);
+ }
+ hr = S_OK;
+ }
+ DeleteObject(bg_brush);
+ return hr;
+}
+
+// static
+NativeThemeWin::ThemeName NativeThemeWin::GetThemeName(Part part) {
+ ThemeName name;
+ switch (part) {
+ case kCheckbox:
+ case kRadio:
+ case kPushButton:
+ name = BUTTON;
+ break;
+ case kInnerSpinButton:
+ name = SPIN;
+ break;
+ case kMenuCheck:
+ case kMenuPopupGutter:
+ case kMenuList:
+ case kMenuPopupArrow:
+ case kMenuPopupSeparator:
+ name = MENU;
+ break;
+ case kProgressBar:
+ name = PROGRESS;
+ break;
+ case kScrollbarDownArrow:
+ case kScrollbarLeftArrow:
+ case kScrollbarRightArrow:
+ case kScrollbarUpArrow:
+ case kScrollbarHorizontalThumb:
+ case kScrollbarVerticalThumb:
+ case kScrollbarHorizontalTrack:
+ case kScrollbarVerticalTrack:
+ name = SCROLLBAR;
+ break;
+ case kSliderTrack:
+ case kSliderThumb:
+ name = TRACKBAR;
+ break;
+ case kTextField:
+ name = TEXTFIELD;
+ break;
+ case kWindowResizeGripper:
+ name = STATUS;
+ break;
+ default:
+ NOTREACHED() << "Invalid part: " << part;
+ break;
+ }
+ return name;
+}
+
+// static
+int NativeThemeWin::GetWindowsPart(Part part,
+ State state,
+ const ExtraParams& extra) {
+ int part_id;
+ switch (part) {
+ case kCheckbox:
+ part_id = BP_CHECKBOX;
+ break;
+ case kMenuCheck:
+ part_id = MENU_POPUPCHECK;
+ break;
+ case kMenuPopupArrow:
+ part_id = MENU_POPUPSUBMENU;
+ break;
+ case kMenuPopupGutter:
+ part_id = MENU_POPUPGUTTER;
+ break;
+ case kMenuPopupSeparator:
+ part_id = MENU_POPUPSEPARATOR;
+ break;
+ case kPushButton:
+ part_id = BP_PUSHBUTTON;
+ break;
+ case kRadio:
+ part_id = BP_RADIOBUTTON;
+ break;
+ case kWindowResizeGripper:
+ part_id = SP_GRIPPER;
+ break;
+ case kScrollbarDownArrow:
+ case kScrollbarLeftArrow:
+ case kScrollbarRightArrow:
+ case kScrollbarUpArrow:
+ part_id = SBP_ARROWBTN;
+ break;
+ case kScrollbarHorizontalThumb:
+ part_id = extra.scrollbar_track.is_upper ? SBP_UPPERTRACKHORZ :
+ SBP_LOWERTRACKHORZ;
+ break;
+ case kScrollbarVerticalThumb:
+ part_id = extra.scrollbar_track.is_upper ? SBP_UPPERTRACKVERT :
+ SBP_LOWERTRACKVERT;
+ break;
+ default:
+ NOTREACHED() << "Invalid part: " << part;
+ break;
+ }
+ return part_id;
+}
+
+int NativeThemeWin::GetWindowsState(Part part,
+ State state,
+ const ExtraParams& extra) {
+ int state_id;
+ switch (part) {
+ case kCheckbox:
+ switch (state) {
+ case kNormal:
+ state_id = CBS_UNCHECKEDNORMAL;
+ break;
+ case kHovered:
+ state_id = CBS_UNCHECKEDHOT;
+ break;
+ case kPressed:
+ state_id = CBS_UNCHECKEDPRESSED;
+ break;
+ case kDisabled:
+ state_id = CBS_UNCHECKEDDISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kMenuCheck:
+ switch (state) {
+ case kNormal:
+ case kHovered:
+ case kPressed:
+ state_id = extra.menu_check.is_radio ? MC_BULLETNORMAL
+ : MC_CHECKMARKNORMAL;
+ break;
+ case kDisabled:
+ state_id = extra.menu_check.is_radio ? MC_BULLETDISABLED
+ : MC_CHECKMARKDISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kMenuPopupArrow:
+ case kMenuPopupGutter:
+ case kMenuPopupSeparator:
+ switch (state) {
+ case kNormal:
+ state_id = MBI_NORMAL;
+ break;
+ case kHovered:
+ state_id = MBI_HOT;
+ break;
+ case kPressed:
+ state_id = MBI_PUSHED;
+ break;
+ case kDisabled:
+ state_id = MBI_DISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kPushButton:
+ switch (state) {
+ case kNormal:
+ state_id = PBS_NORMAL;
+ break;
+ case kHovered:
+ state_id = PBS_HOT;
+ break;
+ case kPressed:
+ state_id = PBS_PRESSED;
+ break;
+ case kDisabled:
+ state_id = PBS_DISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kRadio:
+ switch (state) {
+ case kNormal:
+ state_id = RBS_UNCHECKEDNORMAL;
+ break;
+ case kHovered:
+ state_id = RBS_UNCHECKEDHOT;
+ break;
+ case kPressed:
+ state_id = RBS_UNCHECKEDPRESSED;
+ break;
+ case kDisabled:
+ state_id = RBS_UNCHECKEDDISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kWindowResizeGripper:
+ switch (state) {
+ case kNormal:
+ case kHovered:
+ case kPressed:
+ case kDisabled:
+ state_id = 1; // gripper has no windows state
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kScrollbarDownArrow:
+ switch (state) {
+ case kNormal:
+ state_id = ABS_DOWNNORMAL;
+ break;
+ case kHovered:
+ // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
+ state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
+ ABS_DOWNHOT : ABS_DOWNHOVER;
+ break;
+ case kPressed:
+ state_id = ABS_DOWNPRESSED;
+ break;
+ case kDisabled:
+ state_id = ABS_DOWNDISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kScrollbarLeftArrow:
+ switch (state) {
+ case kNormal:
+ state_id = ABS_LEFTNORMAL;
+ break;
+ case kHovered:
+ // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
+ state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
+ ABS_LEFTHOT : ABS_LEFTHOVER;
+ break;
+ case kPressed:
+ state_id = ABS_LEFTPRESSED;
+ break;
+ case kDisabled:
+ state_id = ABS_LEFTDISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kScrollbarRightArrow:
+ switch (state) {
+ case kNormal:
+ state_id = ABS_RIGHTNORMAL;
+ break;
+ case kHovered:
+ // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
+ state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
+ ABS_RIGHTHOT : ABS_RIGHTHOVER;
+ break;
+ case kPressed:
+ state_id = ABS_RIGHTPRESSED;
+ break;
+ case kDisabled:
+ state_id = ABS_RIGHTDISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kScrollbarUpArrow:
+ switch (state) {
+ case kNormal:
+ state_id = ABS_UPNORMAL;
+ break;
+ case kHovered:
+ // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
+ state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
+ ABS_UPHOT : ABS_UPHOVER;
+ break;
+ case kPressed:
+ state_id = ABS_UPPRESSED;
+ break;
+ case kDisabled:
+ state_id = ABS_UPDISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kScrollbarHorizontalThumb:
+ case kScrollbarVerticalThumb:
+ switch (state) {
+ case kNormal:
+ state_id = SCRBS_NORMAL;
+ break;
+ case kHovered:
+ // Mimic WebKit's behaviour in ScrollbarThemeChromiumWin.cpp.
+ state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
+ SCRBS_HOT : SCRBS_HOVER;
+ break;
+ case kPressed:
+ state_id = SCRBS_PRESSED;
+ break;
+ case kDisabled:
+ state_id = SCRBS_DISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ default:
+ NOTREACHED() << "Invalid part: " << part;
+ break;
+ }
+ return state_id;
+}
+
+HRESULT NativeThemeWin::GetThemeInt(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int *value) const {
+ HANDLE handle = GetThemeHandle(theme);
+ if (handle && get_theme_int_)
+ return get_theme_int_(handle, part_id, state_id, prop_id, value);
+ return E_NOTIMPL;
+}
+
+HRESULT NativeThemeWin::PaintFrameControl(HDC hdc,
+ const gfx::Rect& rect,
+ UINT type,
+ UINT state,
+ bool is_selected,
+ State control_state) const {
+ const int width = rect.width();
+ const int height = rect.height();
+
+ // DrawFrameControl for menu arrow/check wants a monochrome bitmap.
+ base::win::ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL));
+
+ if (mask_bitmap == NULL)
+ return E_OUTOFMEMORY;
+
+ base::win::ScopedCreateDC bitmap_dc(CreateCompatibleDC(NULL));
+ base::win::ScopedSelectObject select_bitmap(bitmap_dc, mask_bitmap);
+ RECT local_rect = { 0, 0, width, height };
+ DrawFrameControl(bitmap_dc, &local_rect, type, state);
+
+ // We're going to use BitBlt with a b&w mask. This results in using the dest
+ // dc's text color for the black bits in the mask, and the dest dc's
+ // background color for the white bits in the mask. DrawFrameControl draws the
+ // check in black, and the background in white.
+ int bg_color_key;
+ int text_color_key;
+ switch (control_state) {
+ case NativeTheme::kHovered:
+ bg_color_key = COLOR_HIGHLIGHT;
+ text_color_key = COLOR_HIGHLIGHTTEXT;
+ break;
+ case NativeTheme::kNormal:
+ bg_color_key = COLOR_MENU;
+ text_color_key = COLOR_MENUTEXT;
+ break;
+ case NativeTheme::kDisabled:
+ bg_color_key = is_selected ? COLOR_HIGHLIGHT : COLOR_MENU;
+ text_color_key = COLOR_GRAYTEXT;
+ break;
+ default:
+ NOTREACHED();
+ bg_color_key = COLOR_MENU;
+ text_color_key = COLOR_MENUTEXT;
+ break;
+ }
+ COLORREF old_bg_color = SetBkColor(hdc, GetSysColor(bg_color_key));
+ COLORREF old_text_color = SetTextColor(hdc, GetSysColor(text_color_key));
+ BitBlt(hdc, rect.x(), rect.y(), width, height, bitmap_dc, 0, 0, SRCCOPY);
+ SetBkColor(hdc, old_bg_color);
+ SetTextColor(hdc, old_text_color);
+
+ return S_OK;
+}
+
+HANDLE NativeThemeWin::GetThemeHandle(ThemeName theme_name) const {
+ if (!open_theme_ || theme_name < 0 || theme_name >= LAST)
+ return 0;
+
+ if (theme_handles_[theme_name])
+ return theme_handles_[theme_name];
+
+ // Not found, try to load it.
+ HANDLE handle = 0;
+ switch (theme_name) {
+ case BUTTON:
+ handle = open_theme_(NULL, L"Button");
+ break;
+ case LIST:
+ handle = open_theme_(NULL, L"Listview");
+ break;
+ case MENU:
+ handle = open_theme_(NULL, L"Menu");
+ break;
+ case MENULIST:
+ handle = open_theme_(NULL, L"Combobox");
+ break;
+ case SCROLLBAR:
+ handle = open_theme_(NULL, L"Scrollbar");
+ break;
+ case STATUS:
+ handle = open_theme_(NULL, L"Status");
+ break;
+ case TAB:
+ handle = open_theme_(NULL, L"Tab");
+ break;
+ case TEXTFIELD:
+ handle = open_theme_(NULL, L"Edit");
+ break;
+ case TRACKBAR:
+ handle = open_theme_(NULL, L"Trackbar");
+ break;
+ case WINDOW:
+ handle = open_theme_(NULL, L"Window");
+ break;
+ case PROGRESS:
+ handle = open_theme_(NULL, L"Progress");
+ break;
+ case SPIN:
+ handle = open_theme_(NULL, L"Spin");
+ break;
+ default:
+ NOTREACHED();
+ }
+ theme_handles_[theme_name] = handle;
+ return handle;
+}
+
+} // namespace ui