Mailbox support for texture layers.
[chromium-blink-merge.git] / ui / native_theme / native_theme_win.cc
blob0293405b1096d917b80a73d91622ac9bd0eeaaf3
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/native_theme/native_theme_win.h"
7 #include <windows.h>
8 #include <uxtheme.h>
9 #include <vsstyle.h>
10 #include <vssym32.h>
12 #include "base/basictypes.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_handle.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/win/scoped_gdi_object.h"
17 #include "base/win/scoped_hdc.h"
18 #include "base/win/scoped_select_object.h"
19 #include "base/win/windows_version.h"
20 #include "skia/ext/bitmap_platform_device.h"
21 #include "skia/ext/platform_canvas.h"
22 #include "skia/ext/skia_utils_win.h"
23 #include "third_party/skia/include/core/SkCanvas.h"
24 #include "third_party/skia/include/core/SkColorPriv.h"
25 #include "third_party/skia/include/core/SkShader.h"
26 #include "ui/gfx/color_utils.h"
27 #include "ui/gfx/gdi_util.h"
28 #include "ui/gfx/rect.h"
29 #include "ui/native_theme/common_theme.h"
31 namespace {
33 // TODO: Obtain the correct colors using GetSysColor.
34 // Theme colors returned by GetSystemColor().
35 const SkColor kInvalidColorIdColor = SkColorSetRGB(255, 0, 128);
36 // Dialogs:
37 const SkColor kDialogBackgroundColor = SkColorSetRGB(251, 251, 251);
38 // FocusableBorder:
39 const SkColor kFocusedBorderColor = SkColorSetRGB(0x4d, 0x90, 0xfe);
40 const SkColor kUnfocusedBorderColor = SkColorSetRGB(0xd9, 0xd9, 0xd9);
41 // TextButton:
42 const SkColor kTextButtonBackgroundColor = SkColorSetRGB(0xde, 0xde, 0xde);
43 const SkColor kTextButtonHighlightColor = SkColorSetARGB(200, 255, 255, 255);
44 const SkColor kTextButtonHoverColor = SkColorSetRGB(6, 45, 117);
45 // MenuItem:
46 const SkColor kEnabledMenuItemForegroundColor = SkColorSetRGB(6, 45, 117);
47 const SkColor kDisabledMenuItemForegroundColor = SkColorSetRGB(161, 161, 146);
48 const SkColor kFocusedMenuItemBackgroundColor = SkColorSetRGB(246, 249, 253);
49 const SkColor kMenuSeparatorColor = SkColorSetARGB(50, 0, 0, 0);
50 // Textfield:
51 const SkColor kTextfieldSelectionBackgroundUnfocused = SK_ColorLTGRAY;
53 // Windows system color IDs cached and updated by the native theme.
54 const int kSystemColors[] = {
55 COLOR_3DFACE,
56 COLOR_BTNTEXT,
57 COLOR_GRAYTEXT,
58 COLOR_HIGHLIGHT,
59 COLOR_HIGHLIGHTTEXT,
60 COLOR_SCROLLBAR,
61 COLOR_WINDOW,
62 COLOR_WINDOWTEXT,
65 void SetCheckerboardShader(SkPaint* paint, const RECT& align_rect) {
66 // Create a 2x2 checkerboard pattern using the 3D face and highlight colors.
67 const SkColor face = color_utils::GetSysSkColor(COLOR_3DFACE);
68 const SkColor highlight = color_utils::GetSysSkColor(COLOR_3DHILIGHT);
69 SkColor buffer[] = { face, highlight, highlight, face };
70 // Confusing bit: we first create a temporary bitmap with our desired pattern,
71 // then copy it to another bitmap. The temporary bitmap doesn't take
72 // ownership of the pixel data, and so will point to garbage when this
73 // function returns. The copy will copy the pixel data into a place owned by
74 // the bitmap, which is in turn owned by the shader, etc., so it will live
75 // until we're done using it.
76 SkBitmap temp_bitmap;
77 temp_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
78 temp_bitmap.setPixels(buffer);
79 SkBitmap bitmap;
80 temp_bitmap.copyTo(&bitmap, temp_bitmap.config());
81 skia::RefPtr<SkShader> shader = skia::AdoptRef(
82 SkShader::CreateBitmapShader(
83 bitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
85 // Align the pattern with the upper corner of |align_rect|.
86 SkMatrix matrix;
87 matrix.setTranslate(SkIntToScalar(align_rect.left),
88 SkIntToScalar(align_rect.top));
89 shader->setLocalMatrix(matrix);
90 paint->setShader(shader.get());
93 // <-a->
94 // [ ***** ]
95 // ____ | |
96 // <-a-> <------b----->
97 // a: object_width
98 // b: frame_width
99 // *: animating object
101 // - the animation goes from "[" to "]" repeatedly.
102 // - the animation offset is at first "|"
104 int ComputeAnimationProgress(int frame_width,
105 int object_width,
106 int pixels_per_second,
107 double animated_seconds) {
108 int animation_width = frame_width + object_width;
109 double interval = static_cast<double>(animation_width) / pixels_per_second;
110 double ratio = fmod(animated_seconds, interval) / interval;
111 return static_cast<int>(animation_width * ratio) - object_width;
114 RECT InsetRect(const RECT* rect, int size) {
115 gfx::Rect result(*rect);
116 result.Inset(size, size);
117 return result.ToRECT();
120 } // namespace
122 namespace ui {
124 bool NativeThemeWin::IsThemingActive() const {
125 if (is_theme_active_)
126 return !!is_theme_active_();
127 return false;
130 HRESULT NativeThemeWin::GetThemeColor(ThemeName theme,
131 int part_id,
132 int state_id,
133 int prop_id,
134 SkColor* color) const {
135 HANDLE handle = GetThemeHandle(theme);
136 if (handle && get_theme_color_) {
137 COLORREF color_ref;
138 if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) ==
139 S_OK) {
140 *color = skia::COLORREFToSkColor(color_ref);
141 return S_OK;
144 return E_NOTIMPL;
147 SkColor NativeThemeWin::GetThemeColorWithDefault(ThemeName theme,
148 int part_id,
149 int state_id,
150 int prop_id,
151 int default_sys_color) const {
152 SkColor color;
153 if (GetThemeColor(theme, part_id, state_id, prop_id, &color) != S_OK)
154 color = color_utils::GetSysSkColor(default_sys_color);
155 return color;
158 gfx::Size NativeThemeWin::GetThemeBorderSize(ThemeName theme) const {
159 // For simplicity use the wildcard state==0, part==0, since it works
160 // for the cases we currently depend on.
161 int border;
162 if (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK)
163 return gfx::Size(border, border);
164 else
165 return gfx::Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE));
168 void NativeThemeWin::DisableTheming() const {
169 if (!set_theme_properties_)
170 return;
171 set_theme_properties_(0);
174 void NativeThemeWin::CloseHandles() const {
175 if (!close_theme_)
176 return;
178 for (int i = 0; i < LAST; ++i) {
179 if (theme_handles_[i]) {
180 close_theme_(theme_handles_[i]);
181 theme_handles_[i] = NULL;
186 bool NativeThemeWin::IsClassicTheme(ThemeName name) const {
187 if (!theme_dll_)
188 return true;
190 return !GetThemeHandle(name);
193 // TODO(sky): seems like we should default to NativeThemeWin, but that currently
194 // breaks a couple of tests (FocusTraversalTest.NormalTraversal in
195 // views_unittests).
196 #if !defined(USE_AURA)
197 // static
198 NativeTheme* NativeTheme::instance() {
199 return NativeThemeWin::instance();
201 #endif
203 // static
204 NativeThemeWin* NativeThemeWin::instance() {
205 CR_DEFINE_STATIC_LOCAL(NativeThemeWin, s_native_theme, ());
206 return &s_native_theme;
209 gfx::Size NativeThemeWin::GetPartSize(Part part,
210 State state,
211 const ExtraParams& extra) const {
212 if (IsNewMenuStyleEnabled()) {
213 gfx::Size size = CommonThemeGetPartSize(part, state, extra);
214 if (!size.IsEmpty())
215 return size;
217 SIZE size;
218 int part_id = GetWindowsPart(part, state, extra);
219 int state_id = GetWindowsState(part, state, extra);
221 HDC hdc = GetDC(NULL);
222 HRESULT hr = GetThemePartSize(GetThemeName(part), hdc, part_id, state_id,
223 NULL, TS_TRUE, &size);
224 ReleaseDC(NULL, hdc);
226 if (FAILED(hr)) {
227 // TODO(rogerta): For now, we need to support radio buttons and checkboxes
228 // when theming is not enabled. Support for other parts can be added
229 // if/when needed.
230 switch (part) {
231 case kCheckbox:
232 case kRadio:
233 // TODO(rogerta): I was not able to find any API to get the default
234 // size of these controls, so determined these values empirically.
235 size.cx = 13;
236 size.cy = 13;
237 break;
238 default:
239 size.cx = 0;
240 size.cy = 0;
241 break;
245 return gfx::Size(size.cx, size.cy);
248 void NativeThemeWin::Paint(SkCanvas* canvas,
249 Part part,
250 State state,
251 const gfx::Rect& rect,
252 const ExtraParams& extra) const {
253 if (IsNewMenuStyleEnabled()) {
254 switch (part) {
255 case kMenuPopupGutter:
256 CommonThemePaintMenuGutter(canvas, rect);
257 return;
258 case kMenuPopupSeparator:
259 CommonThemePaintMenuSeparator(canvas, rect, extra.menu_separator);
260 return;
261 case kMenuPopupBackground:
262 CommonThemePaintMenuBackground(canvas, rect);
263 return;
264 case kMenuItemBackground:
265 CommonThemePaintMenuItemBackground(canvas, state, rect);
266 return;
270 bool needs_paint_indirect = false;
271 if (!skia::SupportsPlatformPaint(canvas)) {
272 // This block will only get hit with --enable-accelerated-drawing flag.
273 needs_paint_indirect = true;
274 } else {
275 // Scrollbars on Windows XP and the Windows Classic theme have particularly
276 // problematic alpha values, so always draw them indirectly.
277 switch (part) {
278 case kScrollbarDownArrow:
279 case kScrollbarUpArrow:
280 case kScrollbarLeftArrow:
281 case kScrollbarRightArrow:
282 case kScrollbarHorizontalThumb:
283 case kScrollbarVerticalThumb:
284 case kScrollbarHorizontalGripper:
285 case kScrollbarVerticalGripper:
286 if (!GetThemeHandle(SCROLLBAR))
287 needs_paint_indirect = true;
288 break;
289 default:
290 break;
294 if (needs_paint_indirect)
295 PaintIndirect(canvas, part, state, rect, extra);
296 else
297 PaintDirect(canvas, part, state, rect, extra);
300 NativeThemeWin::NativeThemeWin()
301 : theme_dll_(LoadLibrary(L"uxtheme.dll")),
302 draw_theme_(NULL),
303 draw_theme_ex_(NULL),
304 get_theme_color_(NULL),
305 get_theme_content_rect_(NULL),
306 get_theme_part_size_(NULL),
307 open_theme_(NULL),
308 close_theme_(NULL),
309 set_theme_properties_(NULL),
310 is_theme_active_(NULL),
311 get_theme_int_(NULL),
312 ALLOW_THIS_IN_INITIALIZER_LIST(color_change_listener_(this)) {
313 if (theme_dll_) {
314 draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>(
315 GetProcAddress(theme_dll_, "DrawThemeBackground"));
316 draw_theme_ex_ = reinterpret_cast<DrawThemeBackgroundExPtr>(
317 GetProcAddress(theme_dll_, "DrawThemeBackgroundEx"));
318 get_theme_color_ = reinterpret_cast<GetThemeColorPtr>(
319 GetProcAddress(theme_dll_, "GetThemeColor"));
320 get_theme_content_rect_ = reinterpret_cast<GetThemeContentRectPtr>(
321 GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect"));
322 get_theme_part_size_ = reinterpret_cast<GetThemePartSizePtr>(
323 GetProcAddress(theme_dll_, "GetThemePartSize"));
324 open_theme_ = reinterpret_cast<OpenThemeDataPtr>(
325 GetProcAddress(theme_dll_, "OpenThemeData"));
326 close_theme_ = reinterpret_cast<CloseThemeDataPtr>(
327 GetProcAddress(theme_dll_, "CloseThemeData"));
328 set_theme_properties_ = reinterpret_cast<SetThemeAppPropertiesPtr>(
329 GetProcAddress(theme_dll_, "SetThemeAppProperties"));
330 is_theme_active_ = reinterpret_cast<IsThemeActivePtr>(
331 GetProcAddress(theme_dll_, "IsThemeActive"));
332 get_theme_int_ = reinterpret_cast<GetThemeIntPtr>(
333 GetProcAddress(theme_dll_, "GetThemeInt"));
335 memset(theme_handles_, 0, sizeof(theme_handles_));
337 // Initialize the cached system colors.
338 UpdateSystemColors();
341 NativeThemeWin::~NativeThemeWin() {
342 if (theme_dll_) {
343 // todo (cpu): fix this soon. Making a call to CloseHandles() here breaks
344 // certain tests and the reliability bots.
345 // CloseHandles();
346 FreeLibrary(theme_dll_);
350 void NativeThemeWin::OnSysColorChange() {
351 UpdateSystemColors();
354 void NativeThemeWin::UpdateSystemColors() {
355 for (int i = 0; i < arraysize(kSystemColors); ++i) {
356 system_colors_[kSystemColors[i]] =
357 color_utils::GetSysSkColor(kSystemColors[i]);
361 void NativeThemeWin::PaintDirect(SkCanvas* canvas,
362 Part part,
363 State state,
364 const gfx::Rect& rect,
365 const ExtraParams& extra) const {
366 skia::ScopedPlatformPaint scoped_platform_paint(canvas);
367 HDC hdc = scoped_platform_paint.GetPlatformSurface();
369 switch (part) {
370 case kCheckbox:
371 PaintCheckbox(hdc, part, state, rect, extra.button);
372 break;
373 case kRadio:
374 PaintRadioButton(hdc, part, state, rect, extra.button);
375 break;
376 case kPushButton:
377 PaintPushButton(hdc, part, state, rect, extra.button);
378 break;
379 case kMenuPopupArrow:
380 PaintMenuArrow(hdc, state, rect, extra.menu_arrow);
381 break;
382 case kMenuPopupGutter:
383 PaintMenuGutter(hdc, rect);
384 break;
385 case kMenuPopupSeparator:
386 PaintMenuSeparator(hdc, rect, extra.menu_separator);
387 break;
388 case kMenuPopupBackground:
389 PaintMenuBackground(hdc, rect);
390 break;
391 case kMenuCheck:
392 PaintMenuCheck(hdc, state, rect, extra.menu_check);
393 break;
394 case kMenuCheckBackground:
395 PaintMenuCheckBackground(hdc, state, rect);
396 break;
397 case kMenuItemBackground:
398 PaintMenuItemBackground(hdc, state, rect, extra.menu_item);
399 break;
400 case kMenuList:
401 PaintMenuList(hdc, state, rect, extra.menu_list);
402 break;
403 case kScrollbarDownArrow:
404 case kScrollbarUpArrow:
405 case kScrollbarLeftArrow:
406 case kScrollbarRightArrow:
407 PaintScrollbarArrow(hdc, part, state, rect, extra.scrollbar_arrow);
408 break;
409 case kScrollbarHorizontalTrack:
410 case kScrollbarVerticalTrack:
411 PaintScrollbarTrack(canvas, hdc, part, state, rect,
412 extra.scrollbar_track);
413 break;
414 case kScrollbarHorizontalThumb:
415 case kScrollbarVerticalThumb:
416 case kScrollbarHorizontalGripper:
417 case kScrollbarVerticalGripper:
418 PaintScrollbarThumb(hdc, part, state, rect, extra.scrollbar_thumb);
419 break;
420 case kInnerSpinButton:
421 PaintSpinButton(hdc, part, state, rect, extra.inner_spin);
422 break;
423 case kTrackbarThumb:
424 case kTrackbarTrack:
425 PaintTrackbar(canvas, hdc, part, state, rect, extra.trackbar);
426 break;
427 case kProgressBar:
428 PaintProgressBar(hdc, rect, extra.progress_bar);
429 break;
430 case kWindowResizeGripper:
431 PaintWindowResizeGripper(hdc, rect);
432 break;
433 case kTabPanelBackground:
434 PaintTabPanelBackground(hdc, rect);
435 break;
436 case kTextField:
437 PaintTextField(hdc, part, state, rect, extra.text_field);
438 break;
440 case kSliderTrack:
441 case kSliderThumb:
442 default:
443 // While transitioning NativeThemeWin to the single Paint() entry point,
444 // unsupported parts will DCHECK here.
445 NOTREACHED();
449 SkColor NativeThemeWin::GetSystemColor(ColorId color_id) const {
450 SkColor color;
451 if (IsNewMenuStyleEnabled() &&
452 CommonThemeGetSystemColor(color_id, &color)) {
453 return color;
456 switch (color_id) {
457 // Windows
458 case kColorId_WindowBackground:
459 return system_colors_[COLOR_WINDOW];
461 // Dialogs
462 case kColorId_DialogBackground:
463 return kDialogBackgroundColor;
465 // FocusableBorder
466 case kColorId_FocusedBorderColor:
467 return kFocusedBorderColor;
468 case kColorId_UnfocusedBorderColor:
469 return kUnfocusedBorderColor;
471 // TextButton
472 case kColorId_TextButtonBackgroundColor:
473 return kTextButtonBackgroundColor;
474 case kColorId_TextButtonEnabledColor:
475 return system_colors_[COLOR_BTNTEXT];
476 case kColorId_TextButtonDisabledColor:
477 return system_colors_[COLOR_GRAYTEXT];
478 case kColorId_TextButtonHighlightColor:
479 return kTextButtonHighlightColor;
480 case kColorId_TextButtonHoverColor:
481 return kTextButtonHoverColor;
483 // MenuItem
484 case kColorId_EnabledMenuItemForegroundColor:
485 return kEnabledMenuItemForegroundColor;
486 case kColorId_DisabledMenuItemForegroundColor:
487 return kDisabledMenuItemForegroundColor;
488 case kColorId_FocusedMenuItemBackgroundColor:
489 return kFocusedMenuItemBackgroundColor;
490 case kColorId_MenuSeparatorColor:
491 return kMenuSeparatorColor;
493 // Label
494 case kColorId_LabelEnabledColor:
495 return system_colors_[COLOR_BTNTEXT];
496 case kColorId_LabelDisabledColor:
497 return system_colors_[COLOR_GRAYTEXT];
498 case kColorId_LabelBackgroundColor:
499 return system_colors_[COLOR_WINDOW];
501 // Textfield
502 case kColorId_TextfieldDefaultColor:
503 return system_colors_[COLOR_WINDOWTEXT];
504 case kColorId_TextfieldDefaultBackground:
505 return system_colors_[COLOR_WINDOW];
506 case kColorId_TextfieldReadOnlyColor:
507 return system_colors_[COLOR_GRAYTEXT];
508 case kColorId_TextfieldReadOnlyBackground:
509 return system_colors_[COLOR_3DFACE];
510 case kColorId_TextfieldSelectionColor:
511 return system_colors_[COLOR_HIGHLIGHTTEXT];
512 case kColorId_TextfieldSelectionBackgroundFocused:
513 return system_colors_[COLOR_HIGHLIGHT];
514 case kColorId_TextfieldSelectionBackgroundUnfocused:
515 return kTextfieldSelectionBackgroundUnfocused;
517 default:
518 NOTREACHED() << "Invalid color_id: " << color_id;
519 break;
521 return kInvalidColorIdColor;
524 void NativeThemeWin::PaintIndirect(SkCanvas* canvas,
525 Part part,
526 State state,
527 const gfx::Rect& rect,
528 const ExtraParams& extra) const {
529 // TODO(asvitkine): This path is pretty inefficient - for each paint operation
530 // it creates a new offscreen bitmap Skia canvas. This can
531 // be sped up by doing it only once per part/state and
532 // keeping a cache of the resulting bitmaps.
534 // Create an offscreen canvas that is backed by an HDC.
535 skia::RefPtr<skia::BitmapPlatformDevice> device = skia::AdoptRef(
536 skia::BitmapPlatformDevice::Create(
537 rect.width(), rect.height(), false, NULL));
538 DCHECK(device);
539 SkCanvas offscreen_canvas(device.get());
540 DCHECK(skia::SupportsPlatformPaint(&offscreen_canvas));
542 // Some of the Windows theme drawing operations do not write correct alpha
543 // values for fully-opaque pixels; instead the pixels get alpha 0. This is
544 // especially a problem on Windows XP or when using the Classic theme.
546 // To work-around this, mark all pixels with a placeholder value, to detect
547 // which pixels get touched by the paint operation. After paint, set any
548 // pixels that have alpha 0 to opaque and placeholders to fully-transparent.
549 const SkColor placeholder = SkColorSetARGB(1, 0, 0, 0);
550 offscreen_canvas.clear(placeholder);
552 // Offset destination rects to have origin (0,0).
553 gfx::Rect adjusted_rect(rect.size());
554 ExtraParams adjusted_extra(extra);
555 switch (part) {
556 case kProgressBar:
557 adjusted_extra.progress_bar.value_rect_x = 0;
558 adjusted_extra.progress_bar.value_rect_y = 0;
559 break;
560 case kScrollbarHorizontalTrack:
561 case kScrollbarVerticalTrack:
562 adjusted_extra.scrollbar_track.track_x = 0;
563 adjusted_extra.scrollbar_track.track_y = 0;
564 break;
565 default: break;
567 // Draw the theme controls using existing HDC-drawing code.
568 PaintDirect(&offscreen_canvas,
569 part,
570 state,
571 adjusted_rect,
572 adjusted_extra);
574 // Copy the pixels to a bitmap that has ref-counted pixel storage, which is
575 // necessary to have when drawing to a SkPicture.
576 const SkBitmap& hdc_bitmap =
577 offscreen_canvas.getDevice()->accessBitmap(false);
578 SkBitmap bitmap;
579 hdc_bitmap.copyTo(&bitmap, SkBitmap::kARGB_8888_Config);
581 // Post-process the pixels to fix up the alpha values (see big comment above).
582 const SkPMColor placeholder_value = SkPreMultiplyColor(placeholder);
583 const int pixel_count = rect.width() * rect.height();
584 SkPMColor* pixels = bitmap.getAddr32(0, 0);
585 for (int i = 0; i < pixel_count; i++) {
586 if (pixels[i] == placeholder_value) {
587 // Pixel wasn't touched - make it fully transparent.
588 pixels[i] = SkPackARGB32(0, 0, 0, 0);
589 } else if (SkGetPackedA32(pixels[i]) == 0) {
590 // Pixel was touched but has incorrect alpha of 0, make it fully opaque.
591 pixels[i] = SkPackARGB32(0xFF,
592 SkGetPackedR32(pixels[i]),
593 SkGetPackedG32(pixels[i]),
594 SkGetPackedB32(pixels[i]));
598 // Draw the offscreen bitmap to the destination canvas.
599 canvas->drawBitmap(bitmap, rect.x(), rect.y());
602 HRESULT NativeThemeWin::GetThemePartSize(ThemeName theme_name,
603 HDC hdc,
604 int part_id,
605 int state_id,
606 RECT* rect,
607 int ts,
608 SIZE* size) const {
609 HANDLE handle = GetThemeHandle(theme_name);
610 if (handle && get_theme_part_size_)
611 return get_theme_part_size_(handle, hdc, part_id, state_id, rect, ts, size);
613 return E_NOTIMPL;
616 HRESULT NativeThemeWin::PaintButton(HDC hdc,
617 State state,
618 const ButtonExtraParams& extra,
619 int part_id,
620 int state_id,
621 RECT* rect) const {
622 HANDLE handle = GetThemeHandle(BUTTON);
623 if (handle && draw_theme_)
624 return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
626 // Adjust classic_state based on part, state, and extras.
627 int classic_state = extra.classic_state;
628 switch (part_id) {
629 case BP_CHECKBOX:
630 classic_state |= DFCS_BUTTONCHECK;
631 break;
632 case BP_RADIOBUTTON:
633 classic_state |= DFCS_BUTTONRADIO;
634 break;
635 case BP_PUSHBUTTON:
636 classic_state |= DFCS_BUTTONPUSH;
637 break;
638 default:
639 NOTREACHED() << "Unknown part_id: " << part_id;
640 break;
643 switch (state) {
644 case kDisabled:
645 classic_state |= DFCS_INACTIVE;
646 break;
647 case kPressed:
648 classic_state |= DFCS_PUSHED;
649 break;
650 case kNormal:
651 case kHovered:
652 break;
653 default:
654 NOTREACHED() << "Unknown state: " << state;
655 break;
658 if (extra.checked)
659 classic_state |= DFCS_CHECKED;
661 // Draw it manually.
662 // All pressed states have both low bits set, and no other states do.
663 const bool focused = ((state_id & ETS_FOCUSED) == ETS_FOCUSED);
664 const bool pressed = ((state_id & PBS_PRESSED) == PBS_PRESSED);
665 if ((BP_PUSHBUTTON == part_id) && (pressed || focused)) {
666 // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the
667 // button itself is shrunk by 1 pixel.
668 HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW);
669 if (brush) {
670 FrameRect(hdc, rect, brush);
671 InflateRect(rect, -1, -1);
674 DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state);
676 // Draw the focus rectangle (the dotted line box) only on buttons. For radio
677 // and checkboxes, we let webkit draw the focus rectangle (orange glow).
678 if ((BP_PUSHBUTTON == part_id) && focused) {
679 // The focus rect is inside the button. The exact number of pixels depends
680 // on whether we're in classic mode or using uxtheme.
681 if (handle && get_theme_content_rect_) {
682 get_theme_content_rect_(handle, hdc, part_id, state_id, rect, rect);
683 } else {
684 InflateRect(rect, -GetSystemMetrics(SM_CXEDGE),
685 -GetSystemMetrics(SM_CYEDGE));
687 DrawFocusRect(hdc, rect);
690 return S_OK;
693 HRESULT NativeThemeWin::PaintMenuSeparator(
694 HDC hdc,
695 const gfx::Rect& rect,
696 const MenuSeparatorExtraParams& extra) const {
697 RECT rect_win = rect.ToRECT();
699 HANDLE handle = GetThemeHandle(MENU);
700 if (handle && draw_theme_) {
701 // Delta is needed for non-classic to move separator up slightly.
702 --rect_win.top;
703 --rect_win.bottom;
704 return draw_theme_(handle, hdc, MENU_POPUPSEPARATOR, MPI_NORMAL, &rect_win,
705 NULL);
708 DrawEdge(hdc, &rect_win, EDGE_ETCHED, BF_TOP);
709 return S_OK;
712 HRESULT NativeThemeWin::PaintMenuGutter(HDC hdc,
713 const gfx::Rect& rect) const {
714 RECT rect_win = rect.ToRECT();
715 HANDLE handle = GetThemeHandle(MENU);
716 if (handle && draw_theme_)
717 return draw_theme_(handle, hdc, MENU_POPUPGUTTER, MPI_NORMAL, &rect_win,
718 NULL);
719 return E_NOTIMPL;
722 HRESULT NativeThemeWin::PaintMenuArrow(HDC hdc,
723 State state,
724 const gfx::Rect& rect,
725 const MenuArrowExtraParams& extra)
726 const {
727 int state_id = MSM_NORMAL;
728 if (state == kDisabled)
729 state_id = MSM_DISABLED;
731 HANDLE handle = GetThemeHandle(MENU);
732 RECT rect_win = rect.ToRECT();
733 if (handle && draw_theme_) {
734 if (extra.pointing_right) {
735 return draw_theme_(handle, hdc, MENU_POPUPSUBMENU, state_id, &rect_win,
736 NULL);
737 } else {
738 // There is no way to tell the uxtheme API to draw a left pointing arrow;
739 // it doesn't have a flag equivalent to DFCS_MENUARROWRIGHT. But they
740 // are needed for RTL locales on Vista. So use a memory DC and mirror
741 // the region with GDI's StretchBlt.
742 gfx::Rect r(rect);
743 base::win::ScopedCreateDC mem_dc(CreateCompatibleDC(hdc));
744 base::win::ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(),
745 r.height()));
746 base::win::ScopedSelectObject select_bitmap(mem_dc, mem_bitmap);
747 // Copy and horizontally mirror the background from hdc into mem_dc. Use
748 // a negative-width source rect, starting at the rightmost pixel.
749 StretchBlt(mem_dc, 0, 0, r.width(), r.height(),
750 hdc, r.right()-1, r.y(), -r.width(), r.height(), SRCCOPY);
751 // Draw the arrow.
752 RECT theme_rect = {0, 0, r.width(), r.height()};
753 HRESULT result = draw_theme_(handle, mem_dc, MENU_POPUPSUBMENU,
754 state_id, &theme_rect, NULL);
755 // Copy and mirror the result back into mem_dc.
756 StretchBlt(hdc, r.x(), r.y(), r.width(), r.height(),
757 mem_dc, r.width()-1, 0, -r.width(), r.height(), SRCCOPY);
758 return result;
762 // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
763 // left pointing arrow. This makes the following 'if' statement slightly
764 // counterintuitive.
765 UINT pfc_state;
766 if (extra.pointing_right)
767 pfc_state = DFCS_MENUARROW;
768 else
769 pfc_state = DFCS_MENUARROWRIGHT;
770 return PaintFrameControl(hdc, rect, DFC_MENU, pfc_state, extra.is_selected,
771 state);
774 HRESULT NativeThemeWin::PaintMenuBackground(HDC hdc,
775 const gfx::Rect& rect) const {
776 HANDLE handle = GetThemeHandle(MENU);
777 RECT rect_win = rect.ToRECT();
778 if (handle && draw_theme_) {
779 HRESULT result = draw_theme_(handle, hdc, MENU_POPUPBACKGROUND, 0,
780 &rect_win, NULL);
781 FrameRect(hdc, &rect_win, GetSysColorBrush(COLOR_3DSHADOW));
782 return result;
785 FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_MENU));
786 DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT);
787 return S_OK;
790 HRESULT NativeThemeWin::PaintMenuCheck(
791 HDC hdc,
792 State state,
793 const gfx::Rect& rect,
794 const MenuCheckExtraParams& extra) const {
795 HANDLE handle = GetThemeHandle(MENU);
796 int state_id;
797 if (extra.is_radio) {
798 state_id = state == kDisabled ? MC_BULLETDISABLED : MC_BULLETNORMAL;
799 } else {
800 state_id = state == kDisabled ? MC_CHECKMARKDISABLED : MC_CHECKMARKNORMAL;
803 RECT rect_win = rect.ToRECT();
804 if (handle && draw_theme_)
805 return draw_theme_(handle, hdc, MENU_POPUPCHECK, state_id, &rect_win, NULL);
807 return PaintFrameControl(hdc, rect, DFC_MENU,
808 extra.is_radio ? DFCS_MENUBULLET : DFCS_MENUCHECK,
809 extra.is_selected, state);
812 HRESULT NativeThemeWin::PaintMenuCheckBackground(HDC hdc,
813 State state,
814 const gfx::Rect& rect) const {
815 HANDLE handle = GetThemeHandle(MENU);
816 int state_id = state == kDisabled ? MCB_DISABLED : MCB_NORMAL;
817 RECT rect_win = rect.ToRECT();
818 if (handle && draw_theme_)
819 return draw_theme_(handle, hdc, MENU_POPUPCHECKBACKGROUND, state_id,
820 &rect_win, NULL);
821 // Nothing to do for background.
822 return S_OK;
825 HRESULT NativeThemeWin::PaintMenuItemBackground(
826 HDC hdc,
827 State state,
828 const gfx::Rect& rect,
829 const MenuItemExtraParams& extra) const {
830 HANDLE handle = GetThemeHandle(MENU);
831 RECT rect_win = rect.ToRECT();
832 int state_id;
833 switch (state) {
834 case kNormal:
835 state_id = MPI_NORMAL;
836 break;
837 case kDisabled:
838 state_id = extra.is_selected ? MPI_DISABLEDHOT : MPI_DISABLED;
839 break;
840 case kHovered:
841 state_id = MPI_HOT;
842 break;
843 default:
844 NOTREACHED() << "Invalid state " << state;
845 break;
848 if (handle && draw_theme_)
849 return draw_theme_(handle, hdc, MENU_POPUPITEM, state_id, &rect_win, NULL);
851 if (extra.is_selected)
852 FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_HIGHLIGHT));
853 return S_OK;
856 HRESULT NativeThemeWin::PaintPushButton(HDC hdc,
857 Part part,
858 State state,
859 const gfx::Rect& rect,
860 const ButtonExtraParams& extra) const {
861 int state_id;
862 switch (state) {
863 case kDisabled:
864 state_id = PBS_DISABLED;
865 break;
866 case kHovered:
867 state_id = PBS_HOT;
868 break;
869 case kNormal:
870 state_id = extra.is_default ? PBS_DEFAULTED : PBS_NORMAL;
871 break;
872 case kPressed:
873 state_id = PBS_PRESSED;
874 break;
875 default:
876 NOTREACHED() << "Invalid state: " << state;
877 break;
880 RECT rect_win = rect.ToRECT();
881 return PaintButton(hdc, state, extra, BP_PUSHBUTTON, state_id, &rect_win);
884 HRESULT NativeThemeWin::PaintRadioButton(HDC hdc,
885 Part part,
886 State state,
887 const gfx::Rect& rect,
888 const ButtonExtraParams& extra) const {
889 int state_id;
890 switch (state) {
891 case kDisabled:
892 state_id = extra.checked ? RBS_CHECKEDDISABLED : RBS_UNCHECKEDDISABLED;
893 break;
894 case kHovered:
895 state_id = extra.checked ? RBS_CHECKEDHOT : RBS_UNCHECKEDHOT;
896 break;
897 case kNormal:
898 state_id = extra.checked ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL;
899 break;
900 case kPressed:
901 state_id = extra.checked ? RBS_CHECKEDPRESSED : RBS_UNCHECKEDPRESSED;
902 break;
903 default:
904 NOTREACHED() << "Invalid state: " << state;
905 break;
908 RECT rect_win = rect.ToRECT();
909 return PaintButton(hdc, state, extra, BP_RADIOBUTTON, state_id, &rect_win);
912 HRESULT NativeThemeWin::PaintCheckbox(HDC hdc,
913 Part part,
914 State state,
915 const gfx::Rect& rect,
916 const ButtonExtraParams& extra) const {
917 int state_id;
918 switch (state) {
919 case kDisabled:
920 state_id = extra.checked ? CBS_CHECKEDDISABLED :
921 extra.indeterminate ? CBS_MIXEDDISABLED :
922 CBS_UNCHECKEDDISABLED;
923 break;
924 case kHovered:
925 state_id = extra.checked ? CBS_CHECKEDHOT :
926 extra.indeterminate ? CBS_MIXEDHOT :
927 CBS_UNCHECKEDHOT;
928 break;
929 case kNormal:
930 state_id = extra.checked ? CBS_CHECKEDNORMAL :
931 extra.indeterminate ? CBS_MIXEDNORMAL :
932 CBS_UNCHECKEDNORMAL;
933 break;
934 case kPressed:
935 state_id = extra.checked ? CBS_CHECKEDPRESSED :
936 extra.indeterminate ? CBS_MIXEDPRESSED :
937 CBS_UNCHECKEDPRESSED;
938 break;
939 default:
940 NOTREACHED() << "Invalid state: " << state;
941 break;
944 RECT rect_win = rect.ToRECT();
945 return PaintButton(hdc, state, extra, BP_CHECKBOX, state_id, &rect_win);
948 HRESULT NativeThemeWin::PaintMenuList(HDC hdc,
949 State state,
950 const gfx::Rect& rect,
951 const MenuListExtraParams& extra) const {
952 HANDLE handle = GetThemeHandle(MENULIST);
953 RECT rect_win = rect.ToRECT();
954 int state_id;
955 switch (state) {
956 case kNormal:
957 state_id = CBXS_NORMAL;
958 break;
959 case kDisabled:
960 state_id = CBXS_DISABLED;
961 break;
962 case kHovered:
963 state_id = CBXS_HOT;
964 break;
965 case kPressed:
966 state_id = CBXS_PRESSED;
967 break;
968 default:
969 NOTREACHED() << "Invalid state " << state;
970 break;
973 if (handle && draw_theme_)
974 return draw_theme_(handle, hdc, CP_DROPDOWNBUTTON, state_id, &rect_win,
975 NULL);
977 // Draw it manually.
978 DrawFrameControl(hdc, &rect_win, DFC_SCROLL,
979 DFCS_SCROLLCOMBOBOX | extra.classic_state);
980 return S_OK;
983 HRESULT NativeThemeWin::PaintScrollbarArrow(
984 HDC hdc,
985 Part part,
986 State state,
987 const gfx::Rect& rect,
988 const ScrollbarArrowExtraParams& extra) const {
989 static const int state_id_matrix[4][kMaxState] = {
990 ABS_DOWNDISABLED, ABS_DOWNHOT, ABS_DOWNNORMAL, ABS_DOWNPRESSED,
991 ABS_LEFTDISABLED, ABS_LEFTHOT, ABS_LEFTNORMAL, ABS_LEFTPRESSED,
992 ABS_RIGHTDISABLED, ABS_RIGHTHOT, ABS_RIGHTNORMAL, ABS_RIGHTPRESSED,
993 ABS_UPDISABLED, ABS_UPHOT, ABS_UPNORMAL, ABS_UPPRESSED
995 HANDLE handle = GetThemeHandle(SCROLLBAR);
996 RECT rect_win = rect.ToRECT();
997 if (handle && draw_theme_) {
998 int index = part - kScrollbarDownArrow;
999 DCHECK(index >=0 && index < 4);
1000 int state_id = state_id_matrix[index][state];
1002 // Hovering means that the cursor is over the scroolbar, but not over the
1003 // specific arrow itself. We don't want to show it "hot" mode, but only
1004 // in "hover" mode.
1005 if (state == kHovered && extra.is_hovering) {
1006 switch (part) {
1007 case kScrollbarDownArrow:
1008 state_id = ABS_DOWNHOVER;
1009 break;
1010 case kScrollbarLeftArrow:
1011 state_id = ABS_LEFTHOVER;
1012 break;
1013 case kScrollbarRightArrow:
1014 state_id = ABS_RIGHTHOVER;
1015 break;
1016 case kScrollbarUpArrow:
1017 state_id = ABS_UPHOVER;
1018 break;
1019 default:
1020 NOTREACHED() << "Invalid part: " << part;
1021 break;
1025 return draw_theme_(handle, hdc, SBP_ARROWBTN, state_id, &rect_win, NULL);
1028 int classic_state = DFCS_SCROLLDOWN;
1029 switch (part) {
1030 case kScrollbarDownArrow:
1031 classic_state = DFCS_SCROLLDOWN;
1032 break;
1033 case kScrollbarLeftArrow:
1034 classic_state = DFCS_SCROLLLEFT;
1035 break;
1036 case kScrollbarRightArrow:
1037 classic_state = DFCS_SCROLLRIGHT;
1038 break;
1039 case kScrollbarUpArrow:
1040 classic_state = DFCS_SCROLLUP;
1041 break;
1042 default:
1043 NOTREACHED() << "Invalid part: " << part;
1044 break;
1046 switch (state) {
1047 case kDisabled:
1048 classic_state |= DFCS_INACTIVE;
1049 break;
1050 case kHovered:
1051 classic_state |= DFCS_HOT;
1052 break;
1053 case kNormal:
1054 break;
1055 case kPressed:
1056 classic_state |= DFCS_PUSHED;
1057 break;
1058 default:
1059 NOTREACHED() << "Invalid state: " << state;
1060 break;
1062 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, classic_state);
1063 return S_OK;
1066 HRESULT NativeThemeWin::PaintScrollbarThumb(
1067 HDC hdc,
1068 Part part,
1069 State state,
1070 const gfx::Rect& rect,
1071 const ScrollbarThumbExtraParams& extra) const {
1072 HANDLE handle = GetThemeHandle(SCROLLBAR);
1073 RECT rect_win = rect.ToRECT();
1074 int part_id;
1075 int state_id;
1077 switch (part) {
1078 case NativeTheme::kScrollbarHorizontalThumb:
1079 part_id = SBP_THUMBBTNHORZ;
1080 break;
1081 case NativeTheme::kScrollbarVerticalThumb:
1082 part_id = SBP_THUMBBTNVERT;
1083 break;
1084 case NativeTheme::kScrollbarHorizontalGripper:
1085 part_id = SBP_GRIPPERHORZ;
1086 break;
1087 case NativeTheme::kScrollbarVerticalGripper:
1088 part_id = SBP_GRIPPERVERT;
1089 break;
1090 default:
1091 NOTREACHED() << "Invalid part: " << part;
1092 break;
1095 switch (state) {
1096 case kDisabled:
1097 state_id = SCRBS_DISABLED;
1098 break;
1099 case kHovered:
1100 state_id = extra.is_hovering ? SCRBS_HOVER : SCRBS_HOT;
1101 break;
1102 case kNormal:
1103 state_id = SCRBS_NORMAL;
1104 break;
1105 case kPressed:
1106 state_id = SCRBS_PRESSED;
1107 break;
1108 default:
1109 NOTREACHED() << "Invalid state: " << state;
1110 break;
1113 if (handle && draw_theme_)
1114 return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
1116 // Draw it manually.
1117 if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT))
1118 DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_MIDDLE);
1119 // Classic mode doesn't have a gripper.
1120 return S_OK;
1123 HRESULT NativeThemeWin::PaintScrollbarTrack(
1124 SkCanvas* canvas,
1125 HDC hdc,
1126 Part part,
1127 State state,
1128 const gfx::Rect& rect,
1129 const ScrollbarTrackExtraParams& extra) const {
1130 HANDLE handle = GetThemeHandle(SCROLLBAR);
1131 RECT rect_win = rect.ToRECT();
1132 int part_id;
1133 int state_id;
1135 switch (part) {
1136 case NativeTheme::kScrollbarHorizontalTrack:
1137 part_id = extra.is_upper ? SBP_UPPERTRACKHORZ : SBP_LOWERTRACKHORZ;
1138 break;
1139 case NativeTheme::kScrollbarVerticalTrack:
1140 part_id = extra.is_upper ? SBP_UPPERTRACKVERT : SBP_LOWERTRACKVERT;
1141 break;
1142 default:
1143 NOTREACHED() << "Invalid part: " << part;
1144 break;
1147 switch (state) {
1148 case kDisabled:
1149 state_id = SCRBS_DISABLED;
1150 break;
1151 case kHovered:
1152 state_id = SCRBS_HOVER;
1153 break;
1154 case kNormal:
1155 state_id = SCRBS_NORMAL;
1156 break;
1157 case kPressed:
1158 state_id = SCRBS_PRESSED;
1159 break;
1160 default:
1161 NOTREACHED() << "Invalid state: " << state;
1162 break;
1165 if (handle && draw_theme_)
1166 return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
1168 // Draw it manually.
1169 if ((system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_3DFACE]) &&
1170 (system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_WINDOW])) {
1171 FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_SCROLLBAR + 1));
1172 } else {
1173 SkPaint paint;
1174 RECT align_rect = gfx::Rect(extra.track_x, extra.track_y, extra.track_width,
1175 extra.track_height).ToRECT();
1176 SetCheckerboardShader(&paint, align_rect);
1177 canvas->drawIRect(skia::RECTToSkIRect(rect_win), paint);
1179 if (extra.classic_state & DFCS_PUSHED)
1180 InvertRect(hdc, &rect_win);
1181 return S_OK;
1184 HRESULT NativeThemeWin::PaintSpinButton(
1185 HDC hdc,
1186 Part part,
1187 State state,
1188 const gfx::Rect& rect,
1189 const InnerSpinButtonExtraParams& extra) const {
1190 HANDLE handle = GetThemeHandle(SPIN);
1191 RECT rect_win = rect.ToRECT();
1192 int part_id = extra.spin_up ? SPNP_UP : SPNP_DOWN;
1193 int state_id;
1194 switch (state) {
1195 case kDisabled:
1196 state_id = extra.spin_up ? UPS_DISABLED : DNS_DISABLED;
1197 break;
1198 case kHovered:
1199 state_id = extra.spin_up ? UPS_HOT : DNS_HOT;
1200 break;
1201 case kNormal:
1202 state_id = extra.spin_up ? UPS_NORMAL : DNS_NORMAL;
1203 break;
1204 case kPressed:
1205 state_id = extra.spin_up ? UPS_PRESSED : DNS_PRESSED;
1206 break;
1207 default:
1208 NOTREACHED() << "Invalid state " << state;
1209 break;
1212 if (handle && draw_theme_)
1213 return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
1214 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, extra.classic_state);
1215 return S_OK;
1218 HRESULT NativeThemeWin::PaintTrackbar(
1219 SkCanvas* canvas,
1220 HDC hdc,
1221 Part part,
1222 State state,
1223 const gfx::Rect& rect,
1224 const TrackbarExtraParams& extra) const {
1225 int part_id = part == kTrackbarTrack ? TKP_TRACK : TKP_THUMBBOTTOM;
1226 if (extra.vertical)
1227 part_id = part == kTrackbarTrack ? TKP_TRACKVERT : TKP_THUMBVERT;
1229 int state_id = 0;
1230 switch (state) {
1231 case kDisabled:
1232 state_id = TUS_DISABLED;
1233 break;
1234 case kHovered:
1235 state_id = TUS_HOT;
1236 break;
1237 case kNormal:
1238 state_id = TUS_NORMAL;
1239 break;
1240 case kPressed:
1241 state_id = TUS_PRESSED;
1242 break;
1243 default:
1244 NOTREACHED() << "Invalid state " << state;
1245 break;
1248 // Make the channel be 4 px thick in the center of the supplied rect. (4 px
1249 // matches what XP does in various menus; GetThemePartSize() doesn't seem to
1250 // return good values here.)
1251 RECT rect_win = rect.ToRECT();
1252 RECT channel_rect = rect.ToRECT();
1253 const int channel_thickness = 4;
1254 if (part_id == TKP_TRACK) {
1255 channel_rect.top +=
1256 ((channel_rect.bottom - channel_rect.top - channel_thickness) / 2);
1257 channel_rect.bottom = channel_rect.top + channel_thickness;
1258 } else if (part_id == TKP_TRACKVERT) {
1259 channel_rect.left +=
1260 ((channel_rect.right - channel_rect.left - channel_thickness) / 2);
1261 channel_rect.right = channel_rect.left + channel_thickness;
1262 } // else this isn't actually a channel, so |channel_rect| == |rect|.
1264 HANDLE handle = GetThemeHandle(TRACKBAR);
1265 if (handle && draw_theme_)
1266 return draw_theme_(handle, hdc, part_id, state_id, &channel_rect, NULL);
1268 // Classic mode, draw it manually.
1269 if ((part_id == TKP_TRACK) || (part_id == TKP_TRACKVERT)) {
1270 DrawEdge(hdc, &channel_rect, EDGE_SUNKEN, BF_RECT);
1271 } else if (part_id == TKP_THUMBVERT) {
1272 DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE);
1273 } else {
1274 // Split rect into top and bottom pieces.
1275 RECT top_section = rect.ToRECT();
1276 RECT bottom_section = rect.ToRECT();
1277 top_section.bottom -= ((bottom_section.right - bottom_section.left) / 2);
1278 bottom_section.top = top_section.bottom;
1279 DrawEdge(hdc, &top_section, EDGE_RAISED,
1280 BF_LEFT | BF_TOP | BF_RIGHT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1282 // Split triangular piece into two diagonals.
1283 RECT& left_half = bottom_section;
1284 RECT right_half = bottom_section;
1285 right_half.left += ((bottom_section.right - bottom_section.left) / 2);
1286 left_half.right = right_half.left;
1287 DrawEdge(hdc, &left_half, EDGE_RAISED,
1288 BF_DIAGONAL_ENDTOPLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1289 DrawEdge(hdc, &right_half, EDGE_RAISED,
1290 BF_DIAGONAL_ENDBOTTOMLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1292 // If the button is pressed, draw hatching.
1293 if (extra.classic_state & DFCS_PUSHED) {
1294 SkPaint paint;
1295 SetCheckerboardShader(&paint, rect_win);
1297 // Fill all three pieces with the pattern.
1298 canvas->drawIRect(skia::RECTToSkIRect(top_section), paint);
1300 SkScalar left_triangle_top = SkIntToScalar(left_half.top);
1301 SkScalar left_triangle_right = SkIntToScalar(left_half.right);
1302 SkPath left_triangle;
1303 left_triangle.moveTo(SkIntToScalar(left_half.left), left_triangle_top);
1304 left_triangle.lineTo(left_triangle_right, left_triangle_top);
1305 left_triangle.lineTo(left_triangle_right,
1306 SkIntToScalar(left_half.bottom));
1307 left_triangle.close();
1308 canvas->drawPath(left_triangle, paint);
1310 SkScalar right_triangle_left = SkIntToScalar(right_half.left);
1311 SkScalar right_triangle_top = SkIntToScalar(right_half.top);
1312 SkPath right_triangle;
1313 right_triangle.moveTo(right_triangle_left, right_triangle_top);
1314 right_triangle.lineTo(SkIntToScalar(right_half.right),
1315 right_triangle_top);
1316 right_triangle.lineTo(right_triangle_left,
1317 SkIntToScalar(right_half.bottom));
1318 right_triangle.close();
1319 canvas->drawPath(right_triangle, paint);
1322 return S_OK;
1325 HRESULT NativeThemeWin::PaintProgressBar(
1326 HDC hdc,
1327 const gfx::Rect& rect,
1328 const ProgressBarExtraParams& extra) const {
1329 // There is no documentation about the animation speed, frame-rate, nor
1330 // size of moving overlay of the indeterminate progress bar.
1331 // So we just observed real-world programs and guessed following parameters.
1332 const int kDeteminateOverlayPixelsPerSecond = 300;
1333 const int kDeteminateOverlayWidth = 120;
1334 const int kIndeterminateOverlayPixelsPerSecond = 175;
1335 const int kVistaIndeterminateOverlayWidth = 120;
1336 const int kXPIndeterminateOverlayWidth = 55;
1337 // The thickness of the bar frame inside |value_rect|
1338 const int kXPBarPadding = 3;
1340 RECT bar_rect = rect.ToRECT();
1341 RECT value_rect = gfx::Rect(extra.value_rect_x,
1342 extra.value_rect_y,
1343 extra.value_rect_width,
1344 extra.value_rect_height).ToRECT();
1346 bool pre_vista = base::win::GetVersion() < base::win::VERSION_VISTA;
1347 HANDLE handle = GetThemeHandle(PROGRESS);
1348 if (handle && draw_theme_ && draw_theme_ex_) {
1349 draw_theme_(handle, hdc, PP_BAR, 0, &bar_rect, NULL);
1351 int bar_width = bar_rect.right - bar_rect.left;
1352 if (extra.determinate) {
1353 // TODO(morrita): this RTL guess can be wrong.
1354 // We should pass the direction from WebKit side.
1355 bool is_rtl = (bar_rect.right == value_rect.right &&
1356 bar_rect.left != value_rect.left);
1357 // We should care the direction here because PP_CNUNK painting
1358 // is asymmetric.
1359 DTBGOPTS value_draw_options;
1360 value_draw_options.dwSize = sizeof(DTBGOPTS);
1361 value_draw_options.dwFlags = is_rtl ? DTBG_MIRRORDC : 0;
1362 value_draw_options.rcClip = bar_rect;
1364 if (pre_vista) {
1365 // On XP, progress bar is chunk-style and has no glossy effect.
1366 // We need to shrink destination rect to fit the part inside the bar
1367 // with an appropriate margin.
1368 RECT shrunk_value_rect = InsetRect(&value_rect, kXPBarPadding);
1369 draw_theme_ex_(handle, hdc, PP_CHUNK, 0,
1370 &shrunk_value_rect, &value_draw_options);
1371 } else {
1372 // On Vista or later, the progress bar part has a
1373 // single-block value part. It also has glossy effect.
1374 // And the value part has exactly same height as the bar part
1375 // so we don't need to shrink the rect.
1376 draw_theme_ex_(handle, hdc, PP_FILL, 0,
1377 &value_rect, &value_draw_options);
1379 int dx = ComputeAnimationProgress(bar_width,
1380 kDeteminateOverlayWidth,
1381 kDeteminateOverlayPixelsPerSecond,
1382 extra.animated_seconds);
1383 RECT overlay_rect = value_rect;
1384 overlay_rect.left += dx;
1385 overlay_rect.right = overlay_rect.left + kDeteminateOverlayWidth;
1386 draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &value_rect);
1388 } else {
1389 // A glossy overlay for indeterminate progress bar has small pause
1390 // after each animation. We emulate this by adding an invisible margin
1391 // the animation has to traverse.
1392 int width_with_margin = bar_width + kIndeterminateOverlayPixelsPerSecond;
1393 int overlay_width = pre_vista ?
1394 kXPIndeterminateOverlayWidth : kVistaIndeterminateOverlayWidth;
1395 int dx = ComputeAnimationProgress(width_with_margin,
1396 overlay_width,
1397 kIndeterminateOverlayPixelsPerSecond,
1398 extra.animated_seconds);
1399 RECT overlay_rect = bar_rect;
1400 overlay_rect.left += dx;
1401 overlay_rect.right = overlay_rect.left + overlay_width;
1402 if (pre_vista) {
1403 RECT shrunk_rect = InsetRect(&overlay_rect, kXPBarPadding);
1404 RECT shrunk_bar_rect = InsetRect(&bar_rect, kXPBarPadding);
1405 draw_theme_(handle, hdc, PP_CHUNK, 0, &shrunk_rect, &shrunk_bar_rect);
1406 } else {
1407 draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &bar_rect);
1411 return S_OK;
1414 HBRUSH bg_brush = GetSysColorBrush(COLOR_BTNFACE);
1415 HBRUSH fg_brush = GetSysColorBrush(COLOR_BTNSHADOW);
1416 FillRect(hdc, &bar_rect, bg_brush);
1417 FillRect(hdc, &value_rect, fg_brush);
1418 DrawEdge(hdc, &bar_rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1419 return S_OK;
1422 HRESULT NativeThemeWin::PaintWindowResizeGripper(HDC hdc,
1423 const gfx::Rect& rect) const {
1424 HANDLE handle = GetThemeHandle(STATUS);
1425 RECT rect_win = rect.ToRECT();
1426 if (handle && draw_theme_) {
1427 // Paint the status bar gripper. There doesn't seem to be a
1428 // standard gripper in Windows for the space between
1429 // scrollbars. This is pretty close, but it's supposed to be
1430 // painted over a status bar.
1431 return draw_theme_(handle, hdc, SP_GRIPPER, 0, &rect_win, NULL);
1434 // Draw a windows classic scrollbar gripper.
1435 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
1436 return S_OK;
1439 HRESULT NativeThemeWin::PaintTabPanelBackground(HDC hdc,
1440 const gfx::Rect& rect) const {
1441 HANDLE handle = GetThemeHandle(TAB);
1442 RECT rect_win = rect.ToRECT();
1443 if (handle && draw_theme_)
1444 return draw_theme_(handle, hdc, TABP_BODY, 0, &rect_win, NULL);
1446 // Classic just renders a flat color background.
1447 FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
1448 return S_OK;
1451 HRESULT NativeThemeWin::PaintTextField(
1452 HDC hdc,
1453 Part part,
1454 State state,
1455 const gfx::Rect& rect,
1456 const TextFieldExtraParams& extra) const {
1457 int part_id = EP_EDITTEXT;
1458 int state_id = ETS_NORMAL;
1459 switch (state) {
1460 case kNormal:
1461 if (extra.is_read_only) {
1462 state_id = ETS_READONLY;
1463 } else if (extra.is_focused) {
1464 state_id = ETS_FOCUSED;
1465 } else {
1466 state_id = ETS_NORMAL;
1468 break;
1469 case kHovered:
1470 state_id = ETS_HOT;
1471 break;
1472 case kPressed:
1473 state_id = ETS_SELECTED;
1474 break;
1475 case kDisabled:
1476 state_id = ETS_DISABLED;
1477 break;
1478 default:
1479 NOTREACHED() << "Invalid state: " << state;
1480 break;
1483 RECT rect_win = rect.ToRECT();
1484 return PaintTextField(hdc, part_id, state_id, extra.classic_state,
1485 &rect_win,
1486 skia::SkColorToCOLORREF(extra.background_color),
1487 extra.fill_content_area, extra.draw_edges);
1490 HRESULT NativeThemeWin::PaintTextField(HDC hdc,
1491 int part_id,
1492 int state_id,
1493 int classic_state,
1494 RECT* rect,
1495 COLORREF color,
1496 bool fill_content_area,
1497 bool draw_edges) const {
1498 // TODO(ojan): http://b/1210017 Figure out how to give the ability to
1499 // exclude individual edges from being drawn.
1501 HANDLE handle = GetThemeHandle(TEXTFIELD);
1502 // TODO(mpcomplete): can we detect if the color is specified by the user,
1503 // and if not, just use the system color?
1504 // CreateSolidBrush() accepts a RGB value but alpha must be 0.
1505 HBRUSH bg_brush = CreateSolidBrush(color);
1506 HRESULT hr;
1507 // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible
1508 // draw_theme_ex_ is NULL and draw_theme_ is non-null.
1509 if (handle && (draw_theme_ex_ || (draw_theme_ && draw_edges))) {
1510 if (draw_theme_ex_) {
1511 static const DTBGOPTS omit_border_options = {
1512 sizeof(DTBGOPTS),
1513 DTBG_OMITBORDER,
1514 { 0, 0, 0, 0 }
1516 const DTBGOPTS* draw_opts = draw_edges ? NULL : &omit_border_options;
1517 hr = draw_theme_ex_(handle, hdc, part_id, state_id, rect, draw_opts);
1518 } else {
1519 hr = draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
1522 // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
1523 if (fill_content_area && get_theme_content_rect_) {
1524 RECT content_rect;
1525 hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect,
1526 &content_rect);
1527 FillRect(hdc, &content_rect, bg_brush);
1529 } else {
1530 // Draw it manually.
1531 if (draw_edges)
1532 DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1534 if (fill_content_area) {
1535 FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ?
1536 reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1) : bg_brush);
1538 hr = S_OK;
1540 DeleteObject(bg_brush);
1541 return hr;
1544 // static
1545 NativeThemeWin::ThemeName NativeThemeWin::GetThemeName(Part part) {
1546 ThemeName name;
1547 switch (part) {
1548 case kCheckbox:
1549 case kRadio:
1550 case kPushButton:
1551 name = BUTTON;
1552 break;
1553 case kInnerSpinButton:
1554 name = SPIN;
1555 break;
1556 case kMenuCheck:
1557 case kMenuPopupGutter:
1558 case kMenuList:
1559 case kMenuPopupArrow:
1560 case kMenuPopupSeparator:
1561 name = MENU;
1562 break;
1563 case kProgressBar:
1564 name = PROGRESS;
1565 break;
1566 case kScrollbarDownArrow:
1567 case kScrollbarLeftArrow:
1568 case kScrollbarRightArrow:
1569 case kScrollbarUpArrow:
1570 case kScrollbarHorizontalThumb:
1571 case kScrollbarVerticalThumb:
1572 case kScrollbarHorizontalTrack:
1573 case kScrollbarVerticalTrack:
1574 name = SCROLLBAR;
1575 break;
1576 case kSliderTrack:
1577 case kSliderThumb:
1578 name = TRACKBAR;
1579 break;
1580 case kTextField:
1581 name = TEXTFIELD;
1582 break;
1583 case kWindowResizeGripper:
1584 name = STATUS;
1585 break;
1586 default:
1587 NOTREACHED() << "Invalid part: " << part;
1588 break;
1590 return name;
1593 // static
1594 int NativeThemeWin::GetWindowsPart(Part part,
1595 State state,
1596 const ExtraParams& extra) {
1597 int part_id;
1598 switch (part) {
1599 case kCheckbox:
1600 part_id = BP_CHECKBOX;
1601 break;
1602 case kMenuCheck:
1603 part_id = MENU_POPUPCHECK;
1604 break;
1605 case kMenuPopupArrow:
1606 part_id = MENU_POPUPSUBMENU;
1607 break;
1608 case kMenuPopupGutter:
1609 part_id = MENU_POPUPGUTTER;
1610 break;
1611 case kMenuPopupSeparator:
1612 part_id = MENU_POPUPSEPARATOR;
1613 break;
1614 case kPushButton:
1615 part_id = BP_PUSHBUTTON;
1616 break;
1617 case kRadio:
1618 part_id = BP_RADIOBUTTON;
1619 break;
1620 case kWindowResizeGripper:
1621 part_id = SP_GRIPPER;
1622 break;
1623 case kScrollbarDownArrow:
1624 case kScrollbarLeftArrow:
1625 case kScrollbarRightArrow:
1626 case kScrollbarUpArrow:
1627 part_id = SBP_ARROWBTN;
1628 break;
1629 case kScrollbarHorizontalThumb:
1630 part_id = extra.scrollbar_track.is_upper ? SBP_UPPERTRACKHORZ :
1631 SBP_LOWERTRACKHORZ;
1632 break;
1633 case kScrollbarVerticalThumb:
1634 part_id = extra.scrollbar_track.is_upper ? SBP_UPPERTRACKVERT :
1635 SBP_LOWERTRACKVERT;
1636 break;
1637 default:
1638 NOTREACHED() << "Invalid part: " << part;
1639 break;
1641 return part_id;
1644 int NativeThemeWin::GetWindowsState(Part part,
1645 State state,
1646 const ExtraParams& extra) {
1647 int state_id;
1648 switch (part) {
1649 case kCheckbox:
1650 switch (state) {
1651 case kNormal:
1652 state_id = CBS_UNCHECKEDNORMAL;
1653 break;
1654 case kHovered:
1655 state_id = CBS_UNCHECKEDHOT;
1656 break;
1657 case kPressed:
1658 state_id = CBS_UNCHECKEDPRESSED;
1659 break;
1660 case kDisabled:
1661 state_id = CBS_UNCHECKEDDISABLED;
1662 break;
1663 default:
1664 NOTREACHED() << "Invalid state: " << state;
1665 break;
1667 break;
1668 case kMenuCheck:
1669 switch (state) {
1670 case kNormal:
1671 case kHovered:
1672 case kPressed:
1673 state_id = extra.menu_check.is_radio ? MC_BULLETNORMAL
1674 : MC_CHECKMARKNORMAL;
1675 break;
1676 case kDisabled:
1677 state_id = extra.menu_check.is_radio ? MC_BULLETDISABLED
1678 : MC_CHECKMARKDISABLED;
1679 break;
1680 default:
1681 NOTREACHED() << "Invalid state: " << state;
1682 break;
1684 break;
1685 case kMenuPopupArrow:
1686 case kMenuPopupGutter:
1687 case kMenuPopupSeparator:
1688 switch (state) {
1689 case kNormal:
1690 state_id = MBI_NORMAL;
1691 break;
1692 case kHovered:
1693 state_id = MBI_HOT;
1694 break;
1695 case kPressed:
1696 state_id = MBI_PUSHED;
1697 break;
1698 case kDisabled:
1699 state_id = MBI_DISABLED;
1700 break;
1701 default:
1702 NOTREACHED() << "Invalid state: " << state;
1703 break;
1705 break;
1706 case kPushButton:
1707 switch (state) {
1708 case kNormal:
1709 state_id = PBS_NORMAL;
1710 break;
1711 case kHovered:
1712 state_id = PBS_HOT;
1713 break;
1714 case kPressed:
1715 state_id = PBS_PRESSED;
1716 break;
1717 case kDisabled:
1718 state_id = PBS_DISABLED;
1719 break;
1720 default:
1721 NOTREACHED() << "Invalid state: " << state;
1722 break;
1724 break;
1725 case kRadio:
1726 switch (state) {
1727 case kNormal:
1728 state_id = RBS_UNCHECKEDNORMAL;
1729 break;
1730 case kHovered:
1731 state_id = RBS_UNCHECKEDHOT;
1732 break;
1733 case kPressed:
1734 state_id = RBS_UNCHECKEDPRESSED;
1735 break;
1736 case kDisabled:
1737 state_id = RBS_UNCHECKEDDISABLED;
1738 break;
1739 default:
1740 NOTREACHED() << "Invalid state: " << state;
1741 break;
1743 break;
1744 case kWindowResizeGripper:
1745 switch (state) {
1746 case kNormal:
1747 case kHovered:
1748 case kPressed:
1749 case kDisabled:
1750 state_id = 1; // gripper has no windows state
1751 break;
1752 default:
1753 NOTREACHED() << "Invalid state: " << state;
1754 break;
1756 break;
1757 case kScrollbarDownArrow:
1758 switch (state) {
1759 case kNormal:
1760 state_id = ABS_DOWNNORMAL;
1761 break;
1762 case kHovered:
1763 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1764 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1765 ABS_DOWNHOT : ABS_DOWNHOVER;
1766 break;
1767 case kPressed:
1768 state_id = ABS_DOWNPRESSED;
1769 break;
1770 case kDisabled:
1771 state_id = ABS_DOWNDISABLED;
1772 break;
1773 default:
1774 NOTREACHED() << "Invalid state: " << state;
1775 break;
1777 break;
1778 case kScrollbarLeftArrow:
1779 switch (state) {
1780 case kNormal:
1781 state_id = ABS_LEFTNORMAL;
1782 break;
1783 case kHovered:
1784 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1785 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1786 ABS_LEFTHOT : ABS_LEFTHOVER;
1787 break;
1788 case kPressed:
1789 state_id = ABS_LEFTPRESSED;
1790 break;
1791 case kDisabled:
1792 state_id = ABS_LEFTDISABLED;
1793 break;
1794 default:
1795 NOTREACHED() << "Invalid state: " << state;
1796 break;
1798 break;
1799 case kScrollbarRightArrow:
1800 switch (state) {
1801 case kNormal:
1802 state_id = ABS_RIGHTNORMAL;
1803 break;
1804 case kHovered:
1805 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1806 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1807 ABS_RIGHTHOT : ABS_RIGHTHOVER;
1808 break;
1809 case kPressed:
1810 state_id = ABS_RIGHTPRESSED;
1811 break;
1812 case kDisabled:
1813 state_id = ABS_RIGHTDISABLED;
1814 break;
1815 default:
1816 NOTREACHED() << "Invalid state: " << state;
1817 break;
1819 break;
1820 case kScrollbarUpArrow:
1821 switch (state) {
1822 case kNormal:
1823 state_id = ABS_UPNORMAL;
1824 break;
1825 case kHovered:
1826 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1827 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1828 ABS_UPHOT : ABS_UPHOVER;
1829 break;
1830 case kPressed:
1831 state_id = ABS_UPPRESSED;
1832 break;
1833 case kDisabled:
1834 state_id = ABS_UPDISABLED;
1835 break;
1836 default:
1837 NOTREACHED() << "Invalid state: " << state;
1838 break;
1840 break;
1841 case kScrollbarHorizontalThumb:
1842 case kScrollbarVerticalThumb:
1843 switch (state) {
1844 case kNormal:
1845 state_id = SCRBS_NORMAL;
1846 break;
1847 case kHovered:
1848 // Mimic WebKit's behaviour in ScrollbarThemeChromiumWin.cpp.
1849 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1850 SCRBS_HOT : SCRBS_HOVER;
1851 break;
1852 case kPressed:
1853 state_id = SCRBS_PRESSED;
1854 break;
1855 case kDisabled:
1856 state_id = SCRBS_DISABLED;
1857 break;
1858 default:
1859 NOTREACHED() << "Invalid state: " << state;
1860 break;
1862 break;
1863 default:
1864 NOTREACHED() << "Invalid part: " << part;
1865 break;
1867 return state_id;
1870 HRESULT NativeThemeWin::GetThemeInt(ThemeName theme,
1871 int part_id,
1872 int state_id,
1873 int prop_id,
1874 int *value) const {
1875 HANDLE handle = GetThemeHandle(theme);
1876 if (handle && get_theme_int_)
1877 return get_theme_int_(handle, part_id, state_id, prop_id, value);
1878 return E_NOTIMPL;
1881 HRESULT NativeThemeWin::PaintFrameControl(HDC hdc,
1882 const gfx::Rect& rect,
1883 UINT type,
1884 UINT state,
1885 bool is_selected,
1886 State control_state) const {
1887 const int width = rect.width();
1888 const int height = rect.height();
1890 // DrawFrameControl for menu arrow/check wants a monochrome bitmap.
1891 base::win::ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL));
1893 if (mask_bitmap == NULL)
1894 return E_OUTOFMEMORY;
1896 base::win::ScopedCreateDC bitmap_dc(CreateCompatibleDC(NULL));
1897 base::win::ScopedSelectObject select_bitmap(bitmap_dc, mask_bitmap);
1898 RECT local_rect = { 0, 0, width, height };
1899 DrawFrameControl(bitmap_dc, &local_rect, type, state);
1901 // We're going to use BitBlt with a b&w mask. This results in using the dest
1902 // dc's text color for the black bits in the mask, and the dest dc's
1903 // background color for the white bits in the mask. DrawFrameControl draws the
1904 // check in black, and the background in white.
1905 int bg_color_key;
1906 int text_color_key;
1907 switch (control_state) {
1908 case NativeTheme::kHovered:
1909 bg_color_key = COLOR_HIGHLIGHT;
1910 text_color_key = COLOR_HIGHLIGHTTEXT;
1911 break;
1912 case NativeTheme::kNormal:
1913 bg_color_key = COLOR_MENU;
1914 text_color_key = COLOR_MENUTEXT;
1915 break;
1916 case NativeTheme::kDisabled:
1917 bg_color_key = is_selected ? COLOR_HIGHLIGHT : COLOR_MENU;
1918 text_color_key = COLOR_GRAYTEXT;
1919 break;
1920 default:
1921 NOTREACHED();
1922 bg_color_key = COLOR_MENU;
1923 text_color_key = COLOR_MENUTEXT;
1924 break;
1926 COLORREF old_bg_color = SetBkColor(hdc, GetSysColor(bg_color_key));
1927 COLORREF old_text_color = SetTextColor(hdc, GetSysColor(text_color_key));
1928 BitBlt(hdc, rect.x(), rect.y(), width, height, bitmap_dc, 0, 0, SRCCOPY);
1929 SetBkColor(hdc, old_bg_color);
1930 SetTextColor(hdc, old_text_color);
1932 return S_OK;
1935 HANDLE NativeThemeWin::GetThemeHandle(ThemeName theme_name) const {
1936 if (!open_theme_ || theme_name < 0 || theme_name >= LAST)
1937 return 0;
1939 if (theme_handles_[theme_name])
1940 return theme_handles_[theme_name];
1942 // Not found, try to load it.
1943 HANDLE handle = 0;
1944 switch (theme_name) {
1945 case BUTTON:
1946 handle = open_theme_(NULL, L"Button");
1947 break;
1948 case LIST:
1949 handle = open_theme_(NULL, L"Listview");
1950 break;
1951 case MENU:
1952 handle = open_theme_(NULL, L"Menu");
1953 break;
1954 case MENULIST:
1955 handle = open_theme_(NULL, L"Combobox");
1956 break;
1957 case SCROLLBAR:
1958 handle = open_theme_(NULL, L"Scrollbar");
1959 break;
1960 case STATUS:
1961 handle = open_theme_(NULL, L"Status");
1962 break;
1963 case TAB:
1964 handle = open_theme_(NULL, L"Tab");
1965 break;
1966 case TEXTFIELD:
1967 handle = open_theme_(NULL, L"Edit");
1968 break;
1969 case TRACKBAR:
1970 handle = open_theme_(NULL, L"Trackbar");
1971 break;
1972 case WINDOW:
1973 handle = open_theme_(NULL, L"Window");
1974 break;
1975 case PROGRESS:
1976 handle = open_theme_(NULL, L"Progress");
1977 break;
1978 case SPIN:
1979 handle = open_theme_(NULL, L"Spin");
1980 break;
1981 default:
1982 NOTREACHED();
1984 theme_handles_[theme_name] = handle;
1985 return handle;
1988 } // namespace ui