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"
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/gfx/rect_conversions.h"
30 #include "ui/gfx/sys_color_change_listener.h"
31 #include "ui/gfx/win/dpi.h"
32 #include "ui/native_theme/common_theme.h"
34 // This was removed from Winvers.h but is still used.
35 #if !defined(COLOR_MENUHIGHLIGHT)
36 #define COLOR_MENUHIGHLIGHT 29
41 // TODO: Obtain the correct colors using GetSysColor.
42 // Theme colors returned by GetSystemColor().
43 const SkColor kInvalidColorIdColor = SkColorSetRGB(255, 0, 128);
45 const SkColor kDialogBackgroundColor = SkColorSetRGB(251, 251, 251);
47 const SkColor kFocusedBorderColor = SkColorSetRGB(0x4d, 0x90, 0xfe);
48 const SkColor kUnfocusedBorderColor = SkColorSetRGB(0xd9, 0xd9, 0xd9);
50 const SkColor kButtonBackgroundColor = SkColorSetRGB(0xde, 0xde, 0xde);
51 const SkColor kButtonHighlightColor = SkColorSetARGB(200, 255, 255, 255);
52 const SkColor kButtonHoverColor = SkColorSetRGB(6, 45, 117);
54 const SkColor kEnabledMenuItemForegroundColor = SkColorSetRGB(6, 45, 117);
55 const SkColor kDisabledMenuItemForegroundColor = SkColorSetRGB(161, 161, 146);
56 const SkColor kFocusedMenuItemBackgroundColor = SkColorSetRGB(246, 249, 253);
57 const SkColor kMenuSeparatorColor = SkColorSetARGB(50, 0, 0, 0);
59 const SkColor kTreeSelectionBackgroundUnfocused = SkColorSetRGB(240, 240, 240);
61 // Windows system color IDs cached and updated by the native theme.
62 const int kSystemColors[] = {
75 void SetCheckerboardShader(SkPaint* paint, const RECT& align_rect) {
76 // Create a 2x2 checkerboard pattern using the 3D face and highlight colors.
77 const SkColor face = color_utils::GetSysSkColor(COLOR_3DFACE);
78 const SkColor highlight = color_utils::GetSysSkColor(COLOR_3DHILIGHT);
79 SkColor buffer[] = { face, highlight, highlight, face };
80 // Confusing bit: we first create a temporary bitmap with our desired pattern,
81 // then copy it to another bitmap. The temporary bitmap doesn't take
82 // ownership of the pixel data, and so will point to garbage when this
83 // function returns. The copy will copy the pixel data into a place owned by
84 // the bitmap, which is in turn owned by the shader, etc., so it will live
85 // until we're done using it.
87 temp_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
88 temp_bitmap.setPixels(buffer);
90 temp_bitmap.copyTo(&bitmap, temp_bitmap.config());
91 skia::RefPtr<SkShader> shader = skia::AdoptRef(
92 SkShader::CreateBitmapShader(
93 bitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
95 // Align the pattern with the upper corner of |align_rect|.
97 matrix.setTranslate(SkIntToScalar(align_rect.left),
98 SkIntToScalar(align_rect.top));
99 shader->setLocalMatrix(matrix);
100 paint->setShader(shader.get());
106 // <-a-> <------b----->
109 // *: animating object
111 // - the animation goes from "[" to "]" repeatedly.
112 // - the animation offset is at first "|"
114 int ComputeAnimationProgress(int frame_width,
116 int pixels_per_second,
117 double animated_seconds) {
118 int animation_width = frame_width + object_width;
119 double interval = static_cast<double>(animation_width) / pixels_per_second;
120 double ratio = fmod(animated_seconds, interval) / interval;
121 return static_cast<int>(animation_width * ratio) - object_width;
124 RECT InsetRect(const RECT* rect, int size) {
125 gfx::Rect result(*rect);
126 result.Inset(size, size);
127 return result.ToRECT();
130 // Returns true if using a high contrast theme.
131 bool UsingHighContrastTheme() {
133 result.cbSize = sizeof(HIGHCONTRAST);
134 return SystemParametersInfo(SPI_GETHIGHCONTRAST, result.cbSize, &result, 0) &&
135 (result.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON;
142 bool NativeThemeWin::IsThemingActive() const {
143 if (is_theme_active_)
144 return !!is_theme_active_();
148 HRESULT NativeThemeWin::GetThemeColor(ThemeName theme,
152 SkColor* color) const {
153 HANDLE handle = GetThemeHandle(theme);
154 if (handle && get_theme_color_) {
156 if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) ==
158 *color = skia::COLORREFToSkColor(color_ref);
165 SkColor NativeThemeWin::GetThemeColorWithDefault(ThemeName theme,
169 int default_sys_color) const {
171 if (GetThemeColor(theme, part_id, state_id, prop_id, &color) != S_OK)
172 color = color_utils::GetSysSkColor(default_sys_color);
176 gfx::Size NativeThemeWin::GetThemeBorderSize(ThemeName theme) const {
177 // For simplicity use the wildcard state==0, part==0, since it works
178 // for the cases we currently depend on.
180 if (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK)
181 return gfx::Size(border, border);
183 return gfx::Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE));
186 void NativeThemeWin::DisableTheming() const {
187 if (!set_theme_properties_)
189 set_theme_properties_(0);
192 void NativeThemeWin::CloseHandles() const {
196 for (int i = 0; i < LAST; ++i) {
197 if (theme_handles_[i]) {
198 close_theme_(theme_handles_[i]);
199 theme_handles_[i] = NULL;
204 bool NativeThemeWin::IsClassicTheme(ThemeName name) const {
208 return !GetThemeHandle(name);
211 // TODO(sky): seems like we should default to NativeThemeWin, but that currently
212 // breaks a couple of tests (FocusTraversalTest.NormalTraversal in
214 #if !defined(USE_AURA)
216 NativeTheme* NativeTheme::instance() {
217 return NativeThemeWin::instance();
222 NativeThemeWin* NativeThemeWin::instance() {
223 CR_DEFINE_STATIC_LOCAL(NativeThemeWin, s_native_theme, ());
224 return &s_native_theme;
227 gfx::Size NativeThemeWin::GetPartSize(Part part,
229 const ExtraParams& extra) const {
230 gfx::Size part_size = CommonThemeGetPartSize(part, state, extra);
231 if (!part_size.IsEmpty())
234 // The GetThemePartSize call below returns the default size without
235 // accounting for user customization (crbug/218291).
238 case kScrollbarDownArrow:
239 case kScrollbarLeftArrow:
240 case kScrollbarRightArrow:
241 case kScrollbarUpArrow:
242 case kScrollbarHorizontalThumb:
243 case kScrollbarVerticalThumb:
244 case kScrollbarHorizontalTrack:
245 case kScrollbarVerticalTrack:
246 size.cx = size.cy = gfx::win::GetSystemMetricsInDIP(SM_CXVSCROLL);
247 return gfx::Size(size.cx, size.cy);
250 int part_id = GetWindowsPart(part, state, extra);
251 int state_id = GetWindowsState(part, state, extra);
253 HDC hdc = GetDC(NULL);
254 HRESULT hr = GetThemePartSize(GetThemeName(part), hdc, part_id, state_id,
255 NULL, TS_TRUE, &size);
256 ReleaseDC(NULL, hdc);
259 // TODO(rogerta): For now, we need to support radio buttons and checkboxes
260 // when theming is not enabled. Support for other parts can be added
265 // TODO(rogerta): I was not able to find any API to get the default
266 // size of these controls, so determined these values empirically.
277 return gfx::Size(size.cx, size.cy);
280 void NativeThemeWin::Paint(SkCanvas* canvas,
283 const gfx::Rect& rect,
284 const ExtraParams& extra) const {
289 case kMenuPopupGutter:
290 CommonThemePaintMenuGutter(canvas, rect);
292 case kMenuPopupSeparator:
293 CommonThemePaintMenuSeparator(canvas, rect, extra.menu_separator);
295 case kMenuPopupBackground:
296 CommonThemePaintMenuBackground(canvas, rect);
298 case kMenuItemBackground:
299 CommonThemePaintMenuItemBackground(canvas, state, rect);
303 bool needs_paint_indirect = false;
304 if (!skia::SupportsPlatformPaint(canvas)) {
305 // This block will only get hit with --enable-accelerated-drawing flag.
306 needs_paint_indirect = true;
308 // Scrollbar components on Windows Classic theme (on all Windows versions)
309 // have particularly problematic alpha values, so always draw them
310 // indirectly. In addition, scrollbar thumbs and grippers for the Windows XP
311 // theme (available only on Windows XP) also need their alpha values
314 case kScrollbarDownArrow:
315 case kScrollbarUpArrow:
316 case kScrollbarLeftArrow:
317 case kScrollbarRightArrow:
318 if (!GetThemeHandle(SCROLLBAR))
319 needs_paint_indirect = true;
321 case kScrollbarHorizontalThumb:
322 case kScrollbarVerticalThumb:
323 case kScrollbarHorizontalGripper:
324 case kScrollbarVerticalGripper:
325 if (!GetThemeHandle(SCROLLBAR) ||
326 base::win::GetVersion() == base::win::VERSION_XP)
327 needs_paint_indirect = true;
334 if (needs_paint_indirect)
335 PaintIndirect(canvas, part, state, rect, extra);
337 PaintDirect(canvas, part, state, rect, extra);
340 NativeThemeWin::NativeThemeWin()
341 : theme_dll_(LoadLibrary(L"uxtheme.dll")),
343 draw_theme_ex_(NULL),
344 get_theme_color_(NULL),
345 get_theme_content_rect_(NULL),
346 get_theme_part_size_(NULL),
349 set_theme_properties_(NULL),
350 is_theme_active_(NULL),
351 get_theme_int_(NULL),
352 color_change_listener_(this) {
354 draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>(
355 GetProcAddress(theme_dll_, "DrawThemeBackground"));
356 draw_theme_ex_ = reinterpret_cast<DrawThemeBackgroundExPtr>(
357 GetProcAddress(theme_dll_, "DrawThemeBackgroundEx"));
358 get_theme_color_ = reinterpret_cast<GetThemeColorPtr>(
359 GetProcAddress(theme_dll_, "GetThemeColor"));
360 get_theme_content_rect_ = reinterpret_cast<GetThemeContentRectPtr>(
361 GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect"));
362 get_theme_part_size_ = reinterpret_cast<GetThemePartSizePtr>(
363 GetProcAddress(theme_dll_, "GetThemePartSize"));
364 open_theme_ = reinterpret_cast<OpenThemeDataPtr>(
365 GetProcAddress(theme_dll_, "OpenThemeData"));
366 close_theme_ = reinterpret_cast<CloseThemeDataPtr>(
367 GetProcAddress(theme_dll_, "CloseThemeData"));
368 set_theme_properties_ = reinterpret_cast<SetThemeAppPropertiesPtr>(
369 GetProcAddress(theme_dll_, "SetThemeAppProperties"));
370 is_theme_active_ = reinterpret_cast<IsThemeActivePtr>(
371 GetProcAddress(theme_dll_, "IsThemeActive"));
372 get_theme_int_ = reinterpret_cast<GetThemeIntPtr>(
373 GetProcAddress(theme_dll_, "GetThemeInt"));
375 memset(theme_handles_, 0, sizeof(theme_handles_));
377 // Initialize the cached system colors.
378 UpdateSystemColors();
381 NativeThemeWin::~NativeThemeWin() {
383 // todo (cpu): fix this soon. Making a call to CloseHandles() here breaks
384 // certain tests and the reliability bots.
386 FreeLibrary(theme_dll_);
390 void NativeThemeWin::OnSysColorChange() {
391 UpdateSystemColors();
394 void NativeThemeWin::UpdateSystemColors() {
395 for (int i = 0; i < arraysize(kSystemColors); ++i) {
396 system_colors_[kSystemColors[i]] =
397 color_utils::GetSysSkColor(kSystemColors[i]);
401 void NativeThemeWin::PaintDirect(SkCanvas* canvas,
404 const gfx::Rect& rect,
405 const ExtraParams& extra) const {
406 skia::ScopedPlatformPaint scoped_platform_paint(canvas);
407 HDC hdc = scoped_platform_paint.GetPlatformSurface();
411 PaintCheckbox(hdc, part, state, rect, extra.button);
414 PaintRadioButton(hdc, part, state, rect, extra.button);
417 PaintPushButton(hdc, part, state, rect, extra.button);
419 case kMenuPopupArrow:
420 PaintMenuArrow(hdc, state, rect, extra.menu_arrow);
422 case kMenuPopupGutter:
423 PaintMenuGutter(hdc, rect);
425 case kMenuPopupSeparator:
426 PaintMenuSeparator(hdc, rect, extra.menu_separator);
428 case kMenuPopupBackground:
429 PaintMenuBackground(hdc, rect);
432 PaintMenuCheck(hdc, state, rect, extra.menu_check);
434 case kMenuCheckBackground:
435 PaintMenuCheckBackground(hdc, state, rect);
437 case kMenuItemBackground:
438 PaintMenuItemBackground(hdc, state, rect, extra.menu_item);
441 PaintMenuList(hdc, state, rect, extra.menu_list);
443 case kScrollbarDownArrow:
444 case kScrollbarUpArrow:
445 case kScrollbarLeftArrow:
446 case kScrollbarRightArrow:
447 PaintScrollbarArrow(hdc, part, state, rect, extra.scrollbar_arrow);
449 case kScrollbarHorizontalTrack:
450 case kScrollbarVerticalTrack:
451 PaintScrollbarTrack(canvas, hdc, part, state, rect,
452 extra.scrollbar_track);
454 case kScrollbarHorizontalThumb:
455 case kScrollbarVerticalThumb:
456 case kScrollbarHorizontalGripper:
457 case kScrollbarVerticalGripper:
458 PaintScrollbarThumb(hdc, part, state, rect, extra.scrollbar_thumb);
460 case kInnerSpinButton:
461 PaintSpinButton(hdc, part, state, rect, extra.inner_spin);
465 PaintTrackbar(canvas, hdc, part, state, rect, extra.trackbar);
468 PaintProgressBar(hdc, rect, extra.progress_bar);
470 case kWindowResizeGripper:
471 PaintWindowResizeGripper(hdc, rect);
473 case kTabPanelBackground:
474 PaintTabPanelBackground(hdc, rect);
477 PaintTextField(hdc, part, state, rect, extra.text_field);
483 // While transitioning NativeThemeWin to the single Paint() entry point,
484 // unsupported parts will DCHECK here.
489 SkColor NativeThemeWin::GetSystemColor(ColorId color_id) const {
491 if (CommonThemeGetSystemColor(color_id, &color))
496 case kColorId_WindowBackground:
497 return system_colors_[COLOR_WINDOW];
500 case kColorId_DialogBackground:
501 if (gfx::IsInvertedColorScheme())
502 return color_utils::InvertColor(kDialogBackgroundColor);
503 return kDialogBackgroundColor;
506 case kColorId_FocusedBorderColor:
507 return kFocusedBorderColor;
508 case kColorId_UnfocusedBorderColor:
509 return kUnfocusedBorderColor;
512 case kColorId_ButtonBackgroundColor:
513 return kButtonBackgroundColor;
514 case kColorId_ButtonEnabledColor:
515 return system_colors_[COLOR_BTNTEXT];
516 case kColorId_ButtonDisabledColor:
517 return system_colors_[COLOR_GRAYTEXT];
518 case kColorId_ButtonHighlightColor:
519 return kButtonHighlightColor;
520 case kColorId_ButtonHoverColor:
521 return kButtonHoverColor;
524 case kColorId_EnabledMenuItemForegroundColor:
525 return kEnabledMenuItemForegroundColor;
526 case kColorId_DisabledMenuItemForegroundColor:
527 return kDisabledMenuItemForegroundColor;
528 case kColorId_FocusedMenuItemBackgroundColor:
529 return kFocusedMenuItemBackgroundColor;
530 case kColorId_MenuSeparatorColor:
531 return kMenuSeparatorColor;
534 case kColorId_LabelEnabledColor:
535 return system_colors_[COLOR_BTNTEXT];
536 case kColorId_LabelDisabledColor:
537 return system_colors_[COLOR_GRAYTEXT];
538 case kColorId_LabelBackgroundColor:
539 return system_colors_[COLOR_WINDOW];
542 case kColorId_TextfieldDefaultColor:
543 return system_colors_[COLOR_WINDOWTEXT];
544 case kColorId_TextfieldDefaultBackground:
545 return system_colors_[COLOR_WINDOW];
546 case kColorId_TextfieldReadOnlyColor:
547 return system_colors_[COLOR_GRAYTEXT];
548 case kColorId_TextfieldReadOnlyBackground:
549 return system_colors_[COLOR_3DFACE];
550 case kColorId_TextfieldSelectionColor:
551 return system_colors_[COLOR_HIGHLIGHTTEXT];
552 case kColorId_TextfieldSelectionBackgroundFocused:
553 return system_colors_[COLOR_HIGHLIGHT];
556 // NOTE: these aren't right for all themes, but as close as I could get.
557 case kColorId_TreeBackground:
558 return system_colors_[COLOR_WINDOW];
559 case kColorId_TreeText:
560 return system_colors_[COLOR_WINDOWTEXT];
561 case kColorId_TreeSelectedText:
562 return system_colors_[COLOR_HIGHLIGHTTEXT];
563 case kColorId_TreeSelectedTextUnfocused:
564 return system_colors_[COLOR_BTNTEXT];
565 case kColorId_TreeSelectionBackgroundFocused:
566 return system_colors_[COLOR_HIGHLIGHT];
567 case kColorId_TreeSelectionBackgroundUnfocused:
568 return system_colors_[UsingHighContrastTheme() ?
569 COLOR_MENUHIGHLIGHT : COLOR_BTNFACE];
570 case kColorId_TreeArrow:
571 return system_colors_[COLOR_WINDOWTEXT];
574 case kColorId_TableBackground:
575 return system_colors_[COLOR_WINDOW];
576 case kColorId_TableText:
577 return system_colors_[COLOR_WINDOWTEXT];
578 case kColorId_TableSelectedText:
579 return system_colors_[COLOR_HIGHLIGHTTEXT];
580 case kColorId_TableSelectedTextUnfocused:
581 return system_colors_[COLOR_BTNTEXT];
582 case kColorId_TableSelectionBackgroundFocused:
583 return system_colors_[COLOR_HIGHLIGHT];
584 case kColorId_TableSelectionBackgroundUnfocused:
585 return system_colors_[UsingHighContrastTheme() ?
586 COLOR_MENUHIGHLIGHT : COLOR_BTNFACE];
587 case kColorId_TableGroupingIndicatorColor:
588 return system_colors_[COLOR_GRAYTEXT];
594 return kInvalidColorIdColor;
597 void NativeThemeWin::PaintIndirect(SkCanvas* canvas,
600 const gfx::Rect& rect,
601 const ExtraParams& extra) const {
602 // TODO(asvitkine): This path is pretty inefficient - for each paint operation
603 // it creates a new offscreen bitmap Skia canvas. This can
604 // be sped up by doing it only once per part/state and
605 // keeping a cache of the resulting bitmaps.
607 // Create an offscreen canvas that is backed by an HDC.
608 skia::RefPtr<skia::BitmapPlatformDevice> device = skia::AdoptRef(
609 skia::BitmapPlatformDevice::Create(
610 rect.width(), rect.height(), false, NULL));
612 SkCanvas offscreen_canvas(device.get());
613 DCHECK(skia::SupportsPlatformPaint(&offscreen_canvas));
615 // Some of the Windows theme drawing operations do not write correct alpha
616 // values for fully-opaque pixels; instead the pixels get alpha 0. This is
617 // especially a problem on Windows XP or when using the Classic theme.
619 // To work-around this, mark all pixels with a placeholder value, to detect
620 // which pixels get touched by the paint operation. After paint, set any
621 // pixels that have alpha 0 to opaque and placeholders to fully-transparent.
622 const SkColor placeholder = SkColorSetARGB(1, 0, 0, 0);
623 offscreen_canvas.clear(placeholder);
625 // Offset destination rects to have origin (0,0).
626 gfx::Rect adjusted_rect(rect.size());
627 ExtraParams adjusted_extra(extra);
630 adjusted_extra.progress_bar.value_rect_x = 0;
631 adjusted_extra.progress_bar.value_rect_y = 0;
633 case kScrollbarHorizontalTrack:
634 case kScrollbarVerticalTrack:
635 adjusted_extra.scrollbar_track.track_x = 0;
636 adjusted_extra.scrollbar_track.track_y = 0;
640 // Draw the theme controls using existing HDC-drawing code.
641 PaintDirect(&offscreen_canvas,
647 // Copy the pixels to a bitmap that has ref-counted pixel storage, which is
648 // necessary to have when drawing to a SkPicture.
649 const SkBitmap& hdc_bitmap =
650 offscreen_canvas.getDevice()->accessBitmap(false);
652 hdc_bitmap.copyTo(&bitmap, SkBitmap::kARGB_8888_Config);
654 // Post-process the pixels to fix up the alpha values (see big comment above).
655 const SkPMColor placeholder_value = SkPreMultiplyColor(placeholder);
656 const int pixel_count = rect.width() * rect.height();
657 SkPMColor* pixels = bitmap.getAddr32(0, 0);
658 for (int i = 0; i < pixel_count; i++) {
659 if (pixels[i] == placeholder_value) {
660 // Pixel wasn't touched - make it fully transparent.
661 pixels[i] = SkPackARGB32(0, 0, 0, 0);
662 } else if (SkGetPackedA32(pixels[i]) == 0) {
663 // Pixel was touched but has incorrect alpha of 0, make it fully opaque.
664 pixels[i] = SkPackARGB32(0xFF,
665 SkGetPackedR32(pixels[i]),
666 SkGetPackedG32(pixels[i]),
667 SkGetPackedB32(pixels[i]));
671 // Draw the offscreen bitmap to the destination canvas.
672 canvas->drawBitmap(bitmap, rect.x(), rect.y());
675 HRESULT NativeThemeWin::GetThemePartSize(ThemeName theme_name,
682 HANDLE handle = GetThemeHandle(theme_name);
683 if (handle && get_theme_part_size_)
684 return get_theme_part_size_(handle, hdc, part_id, state_id, rect, ts, size);
689 HRESULT NativeThemeWin::PaintButton(HDC hdc,
691 const ButtonExtraParams& extra,
695 HANDLE handle = GetThemeHandle(BUTTON);
696 if (handle && draw_theme_)
697 return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
699 // Adjust classic_state based on part, state, and extras.
700 int classic_state = extra.classic_state;
703 classic_state |= DFCS_BUTTONCHECK;
706 classic_state |= DFCS_BUTTONRADIO;
709 classic_state |= DFCS_BUTTONPUSH;
712 NOTREACHED() << "Unknown part_id: " << part_id;
718 classic_state |= DFCS_INACTIVE;
721 classic_state |= DFCS_PUSHED;
727 NOTREACHED() << "Unknown state: " << state;
732 classic_state |= DFCS_CHECKED;
735 // All pressed states have both low bits set, and no other states do.
736 const bool focused = ((state_id & ETS_FOCUSED) == ETS_FOCUSED);
737 const bool pressed = ((state_id & PBS_PRESSED) == PBS_PRESSED);
738 if ((BP_PUSHBUTTON == part_id) && (pressed || focused)) {
739 // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the
740 // button itself is shrunk by 1 pixel.
741 HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW);
743 FrameRect(hdc, rect, brush);
744 InflateRect(rect, -1, -1);
747 DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state);
749 // Draw the focus rectangle (the dotted line box) only on buttons. For radio
750 // and checkboxes, we let webkit draw the focus rectangle (orange glow).
751 if ((BP_PUSHBUTTON == part_id) && focused) {
752 // The focus rect is inside the button. The exact number of pixels depends
753 // on whether we're in classic mode or using uxtheme.
754 if (handle && get_theme_content_rect_) {
755 get_theme_content_rect_(handle, hdc, part_id, state_id, rect, rect);
757 InflateRect(rect, -GetSystemMetrics(SM_CXEDGE),
758 -GetSystemMetrics(SM_CYEDGE));
760 DrawFocusRect(hdc, rect);
763 // Classic theme doesn't support indeterminate checkboxes. We draw
764 // a recangle inside a checkbox like IE10 does.
765 if (part_id == BP_CHECKBOX && extra.indeterminate) {
766 RECT inner_rect = *rect;
767 // "4 / 13" is same as IE10 in classic theme.
768 int padding = (inner_rect.right - inner_rect.left) * 4 / 13;
769 InflateRect(&inner_rect, -padding, -padding);
770 int color_index = state == kDisabled ? COLOR_GRAYTEXT : COLOR_WINDOWTEXT;
771 FillRect(hdc, &inner_rect, GetSysColorBrush(color_index));
777 HRESULT NativeThemeWin::PaintMenuSeparator(
779 const gfx::Rect& rect,
780 const MenuSeparatorExtraParams& extra) const {
781 RECT rect_win = rect.ToRECT();
783 HANDLE handle = GetThemeHandle(MENU);
784 if (handle && draw_theme_) {
785 // Delta is needed for non-classic to move separator up slightly.
788 return draw_theme_(handle, hdc, MENU_POPUPSEPARATOR, MPI_NORMAL, &rect_win,
792 DrawEdge(hdc, &rect_win, EDGE_ETCHED, BF_TOP);
796 HRESULT NativeThemeWin::PaintMenuGutter(HDC hdc,
797 const gfx::Rect& rect) const {
798 RECT rect_win = rect.ToRECT();
799 HANDLE handle = GetThemeHandle(MENU);
800 if (handle && draw_theme_)
801 return draw_theme_(handle, hdc, MENU_POPUPGUTTER, MPI_NORMAL, &rect_win,
806 HRESULT NativeThemeWin::PaintMenuArrow(HDC hdc,
808 const gfx::Rect& rect,
809 const MenuArrowExtraParams& extra)
811 int state_id = MSM_NORMAL;
812 if (state == kDisabled)
813 state_id = MSM_DISABLED;
815 HANDLE handle = GetThemeHandle(MENU);
816 RECT rect_win = rect.ToRECT();
817 if (handle && draw_theme_) {
818 if (extra.pointing_right) {
819 return draw_theme_(handle, hdc, MENU_POPUPSUBMENU, state_id, &rect_win,
822 // There is no way to tell the uxtheme API to draw a left pointing arrow;
823 // it doesn't have a flag equivalent to DFCS_MENUARROWRIGHT. But they
824 // are needed for RTL locales on Vista. So use a memory DC and mirror
825 // the region with GDI's StretchBlt.
827 base::win::ScopedCreateDC mem_dc(CreateCompatibleDC(hdc));
828 base::win::ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(),
830 base::win::ScopedSelectObject select_bitmap(mem_dc, mem_bitmap);
831 // Copy and horizontally mirror the background from hdc into mem_dc. Use
832 // a negative-width source rect, starting at the rightmost pixel.
833 StretchBlt(mem_dc, 0, 0, r.width(), r.height(),
834 hdc, r.right()-1, r.y(), -r.width(), r.height(), SRCCOPY);
836 RECT theme_rect = {0, 0, r.width(), r.height()};
837 HRESULT result = draw_theme_(handle, mem_dc, MENU_POPUPSUBMENU,
838 state_id, &theme_rect, NULL);
839 // Copy and mirror the result back into mem_dc.
840 StretchBlt(hdc, r.x(), r.y(), r.width(), r.height(),
841 mem_dc, r.width()-1, 0, -r.width(), r.height(), SRCCOPY);
846 // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
847 // left pointing arrow. This makes the following 'if' statement slightly
850 if (extra.pointing_right)
851 pfc_state = DFCS_MENUARROW;
853 pfc_state = DFCS_MENUARROWRIGHT;
854 return PaintFrameControl(hdc, rect, DFC_MENU, pfc_state, extra.is_selected,
858 HRESULT NativeThemeWin::PaintMenuBackground(HDC hdc,
859 const gfx::Rect& rect) const {
860 HANDLE handle = GetThemeHandle(MENU);
861 RECT rect_win = rect.ToRECT();
862 if (handle && draw_theme_) {
863 HRESULT result = draw_theme_(handle, hdc, MENU_POPUPBACKGROUND, 0,
865 FrameRect(hdc, &rect_win, GetSysColorBrush(COLOR_3DSHADOW));
869 FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_MENU));
870 DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT);
874 HRESULT NativeThemeWin::PaintMenuCheck(
877 const gfx::Rect& rect,
878 const MenuCheckExtraParams& extra) const {
879 HANDLE handle = GetThemeHandle(MENU);
881 if (extra.is_radio) {
882 state_id = state == kDisabled ? MC_BULLETDISABLED : MC_BULLETNORMAL;
884 state_id = state == kDisabled ? MC_CHECKMARKDISABLED : MC_CHECKMARKNORMAL;
887 RECT rect_win = rect.ToRECT();
888 if (handle && draw_theme_)
889 return draw_theme_(handle, hdc, MENU_POPUPCHECK, state_id, &rect_win, NULL);
891 return PaintFrameControl(hdc, rect, DFC_MENU,
892 extra.is_radio ? DFCS_MENUBULLET : DFCS_MENUCHECK,
893 extra.is_selected, state);
896 HRESULT NativeThemeWin::PaintMenuCheckBackground(HDC hdc,
898 const gfx::Rect& rect) const {
899 HANDLE handle = GetThemeHandle(MENU);
900 int state_id = state == kDisabled ? MCB_DISABLED : MCB_NORMAL;
901 RECT rect_win = rect.ToRECT();
902 if (handle && draw_theme_)
903 return draw_theme_(handle, hdc, MENU_POPUPCHECKBACKGROUND, state_id,
905 // Nothing to do for background.
909 HRESULT NativeThemeWin::PaintMenuItemBackground(
912 const gfx::Rect& rect,
913 const MenuItemExtraParams& extra) const {
914 HANDLE handle = GetThemeHandle(MENU);
915 RECT rect_win = rect.ToRECT();
919 state_id = MPI_NORMAL;
922 state_id = extra.is_selected ? MPI_DISABLEDHOT : MPI_DISABLED;
928 NOTREACHED() << "Invalid state " << state;
932 if (handle && draw_theme_)
933 return draw_theme_(handle, hdc, MENU_POPUPITEM, state_id, &rect_win, NULL);
935 if (extra.is_selected)
936 FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_HIGHLIGHT));
940 HRESULT NativeThemeWin::PaintPushButton(HDC hdc,
943 const gfx::Rect& rect,
944 const ButtonExtraParams& extra) const {
948 state_id = PBS_DISABLED;
954 state_id = extra.is_default ? PBS_DEFAULTED : PBS_NORMAL;
957 state_id = PBS_PRESSED;
960 NOTREACHED() << "Invalid state: " << state;
964 RECT rect_win = rect.ToRECT();
965 return PaintButton(hdc, state, extra, BP_PUSHBUTTON, state_id, &rect_win);
968 HRESULT NativeThemeWin::PaintRadioButton(HDC hdc,
971 const gfx::Rect& rect,
972 const ButtonExtraParams& extra) const {
976 state_id = extra.checked ? RBS_CHECKEDDISABLED : RBS_UNCHECKEDDISABLED;
979 state_id = extra.checked ? RBS_CHECKEDHOT : RBS_UNCHECKEDHOT;
982 state_id = extra.checked ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL;
985 state_id = extra.checked ? RBS_CHECKEDPRESSED : RBS_UNCHECKEDPRESSED;
988 NOTREACHED() << "Invalid state: " << state;
992 RECT rect_win = rect.ToRECT();
993 return PaintButton(hdc, state, extra, BP_RADIOBUTTON, state_id, &rect_win);
996 HRESULT NativeThemeWin::PaintCheckbox(HDC hdc,
999 const gfx::Rect& rect,
1000 const ButtonExtraParams& extra) const {
1004 state_id = extra.checked ? CBS_CHECKEDDISABLED :
1005 extra.indeterminate ? CBS_MIXEDDISABLED :
1006 CBS_UNCHECKEDDISABLED;
1009 state_id = extra.checked ? CBS_CHECKEDHOT :
1010 extra.indeterminate ? CBS_MIXEDHOT :
1014 state_id = extra.checked ? CBS_CHECKEDNORMAL :
1015 extra.indeterminate ? CBS_MIXEDNORMAL :
1016 CBS_UNCHECKEDNORMAL;
1019 state_id = extra.checked ? CBS_CHECKEDPRESSED :
1020 extra.indeterminate ? CBS_MIXEDPRESSED :
1021 CBS_UNCHECKEDPRESSED;
1024 NOTREACHED() << "Invalid state: " << state;
1028 RECT rect_win = rect.ToRECT();
1029 return PaintButton(hdc, state, extra, BP_CHECKBOX, state_id, &rect_win);
1032 HRESULT NativeThemeWin::PaintMenuList(HDC hdc,
1034 const gfx::Rect& rect,
1035 const MenuListExtraParams& extra) const {
1036 HANDLE handle = GetThemeHandle(MENULIST);
1037 RECT rect_win = rect.ToRECT();
1041 state_id = CBXS_NORMAL;
1044 state_id = CBXS_DISABLED;
1047 state_id = CBXS_HOT;
1050 state_id = CBXS_PRESSED;
1053 NOTREACHED() << "Invalid state " << state;
1057 if (handle && draw_theme_)
1058 return draw_theme_(handle, hdc, CP_DROPDOWNBUTTON, state_id, &rect_win,
1061 // Draw it manually.
1062 DrawFrameControl(hdc, &rect_win, DFC_SCROLL,
1063 DFCS_SCROLLCOMBOBOX | extra.classic_state);
1067 HRESULT NativeThemeWin::PaintScrollbarArrow(
1071 const gfx::Rect& rect,
1072 const ScrollbarArrowExtraParams& extra) const {
1073 static const int state_id_matrix[4][kMaxState] = {
1074 ABS_DOWNDISABLED, ABS_DOWNHOT, ABS_DOWNNORMAL, ABS_DOWNPRESSED,
1075 ABS_LEFTDISABLED, ABS_LEFTHOT, ABS_LEFTNORMAL, ABS_LEFTPRESSED,
1076 ABS_RIGHTDISABLED, ABS_RIGHTHOT, ABS_RIGHTNORMAL, ABS_RIGHTPRESSED,
1077 ABS_UPDISABLED, ABS_UPHOT, ABS_UPNORMAL, ABS_UPPRESSED
1079 HANDLE handle = GetThemeHandle(SCROLLBAR);
1080 RECT rect_win = rect.ToRECT();
1081 if (handle && draw_theme_) {
1082 int index = part - kScrollbarDownArrow;
1083 DCHECK(index >=0 && index < 4);
1084 int state_id = state_id_matrix[index][state];
1086 // Hovering means that the cursor is over the scroolbar, but not over the
1087 // specific arrow itself. We don't want to show it "hot" mode, but only
1089 if (state == kHovered && extra.is_hovering) {
1091 case kScrollbarDownArrow:
1092 state_id = ABS_DOWNHOVER;
1094 case kScrollbarLeftArrow:
1095 state_id = ABS_LEFTHOVER;
1097 case kScrollbarRightArrow:
1098 state_id = ABS_RIGHTHOVER;
1100 case kScrollbarUpArrow:
1101 state_id = ABS_UPHOVER;
1104 NOTREACHED() << "Invalid part: " << part;
1108 return PaintScaledTheme(handle, hdc, SBP_ARROWBTN, state_id, rect);
1111 int classic_state = DFCS_SCROLLDOWN;
1113 case kScrollbarDownArrow:
1114 classic_state = DFCS_SCROLLDOWN;
1116 case kScrollbarLeftArrow:
1117 classic_state = DFCS_SCROLLLEFT;
1119 case kScrollbarRightArrow:
1120 classic_state = DFCS_SCROLLRIGHT;
1122 case kScrollbarUpArrow:
1123 classic_state = DFCS_SCROLLUP;
1126 NOTREACHED() << "Invalid part: " << part;
1131 classic_state |= DFCS_INACTIVE;
1134 classic_state |= DFCS_HOT;
1139 classic_state |= DFCS_PUSHED;
1142 NOTREACHED() << "Invalid state: " << state;
1145 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, classic_state);
1149 HRESULT NativeThemeWin::PaintScrollbarThumb(
1153 const gfx::Rect& rect,
1154 const ScrollbarThumbExtraParams& extra) const {
1155 HANDLE handle = GetThemeHandle(SCROLLBAR);
1156 RECT rect_win = rect.ToRECT();
1161 case NativeTheme::kScrollbarHorizontalThumb:
1162 part_id = SBP_THUMBBTNHORZ;
1164 case NativeTheme::kScrollbarVerticalThumb:
1165 part_id = SBP_THUMBBTNVERT;
1167 case NativeTheme::kScrollbarHorizontalGripper:
1168 part_id = SBP_GRIPPERHORZ;
1170 case NativeTheme::kScrollbarVerticalGripper:
1171 part_id = SBP_GRIPPERVERT;
1174 NOTREACHED() << "Invalid part: " << part;
1180 state_id = SCRBS_DISABLED;
1183 state_id = extra.is_hovering ? SCRBS_HOVER : SCRBS_HOT;
1186 state_id = SCRBS_NORMAL;
1189 state_id = SCRBS_PRESSED;
1192 NOTREACHED() << "Invalid state: " << state;
1196 if (handle && draw_theme_)
1197 return PaintScaledTheme(handle, hdc, part_id, state_id, rect);
1199 // Draw it manually.
1200 if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT))
1201 DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_MIDDLE);
1202 // Classic mode doesn't have a gripper.
1206 HRESULT NativeThemeWin::PaintScrollbarTrack(
1211 const gfx::Rect& rect,
1212 const ScrollbarTrackExtraParams& extra) const {
1213 HANDLE handle = GetThemeHandle(SCROLLBAR);
1214 RECT rect_win = rect.ToRECT();
1219 case NativeTheme::kScrollbarHorizontalTrack:
1220 part_id = extra.is_upper ? SBP_UPPERTRACKHORZ : SBP_LOWERTRACKHORZ;
1222 case NativeTheme::kScrollbarVerticalTrack:
1223 part_id = extra.is_upper ? SBP_UPPERTRACKVERT : SBP_LOWERTRACKVERT;
1226 NOTREACHED() << "Invalid part: " << part;
1232 state_id = SCRBS_DISABLED;
1235 state_id = SCRBS_HOVER;
1238 state_id = SCRBS_NORMAL;
1241 state_id = SCRBS_PRESSED;
1244 NOTREACHED() << "Invalid state: " << state;
1248 if (handle && draw_theme_)
1249 return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
1251 // Draw it manually.
1252 if ((system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_3DFACE]) &&
1253 (system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_WINDOW])) {
1254 FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_SCROLLBAR + 1));
1257 RECT align_rect = gfx::Rect(extra.track_x, extra.track_y, extra.track_width,
1258 extra.track_height).ToRECT();
1259 SetCheckerboardShader(&paint, align_rect);
1260 canvas->drawIRect(skia::RECTToSkIRect(rect_win), paint);
1262 if (extra.classic_state & DFCS_PUSHED)
1263 InvertRect(hdc, &rect_win);
1267 HRESULT NativeThemeWin::PaintSpinButton(
1271 const gfx::Rect& rect,
1272 const InnerSpinButtonExtraParams& extra) const {
1273 HANDLE handle = GetThemeHandle(SPIN);
1274 RECT rect_win = rect.ToRECT();
1275 int part_id = extra.spin_up ? SPNP_UP : SPNP_DOWN;
1279 state_id = extra.spin_up ? UPS_DISABLED : DNS_DISABLED;
1282 state_id = extra.spin_up ? UPS_HOT : DNS_HOT;
1285 state_id = extra.spin_up ? UPS_NORMAL : DNS_NORMAL;
1288 state_id = extra.spin_up ? UPS_PRESSED : DNS_PRESSED;
1291 NOTREACHED() << "Invalid state " << state;
1295 if (handle && draw_theme_)
1296 return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
1297 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, extra.classic_state);
1301 HRESULT NativeThemeWin::PaintTrackbar(
1306 const gfx::Rect& rect,
1307 const TrackbarExtraParams& extra) const {
1308 int part_id = part == kTrackbarTrack ? TKP_TRACK : TKP_THUMBBOTTOM;
1310 part_id = part == kTrackbarTrack ? TKP_TRACKVERT : TKP_THUMBVERT;
1315 state_id = TUS_DISABLED;
1321 state_id = TUS_NORMAL;
1324 state_id = TUS_PRESSED;
1327 NOTREACHED() << "Invalid state " << state;
1331 // Make the channel be 4 px thick in the center of the supplied rect. (4 px
1332 // matches what XP does in various menus; GetThemePartSize() doesn't seem to
1333 // return good values here.)
1334 RECT rect_win = rect.ToRECT();
1335 RECT channel_rect = rect.ToRECT();
1336 const int channel_thickness = 4;
1337 if (part_id == TKP_TRACK) {
1339 ((channel_rect.bottom - channel_rect.top - channel_thickness) / 2);
1340 channel_rect.bottom = channel_rect.top + channel_thickness;
1341 } else if (part_id == TKP_TRACKVERT) {
1342 channel_rect.left +=
1343 ((channel_rect.right - channel_rect.left - channel_thickness) / 2);
1344 channel_rect.right = channel_rect.left + channel_thickness;
1345 } // else this isn't actually a channel, so |channel_rect| == |rect|.
1347 HANDLE handle = GetThemeHandle(TRACKBAR);
1348 if (handle && draw_theme_)
1349 return draw_theme_(handle, hdc, part_id, state_id, &channel_rect, NULL);
1351 // Classic mode, draw it manually.
1352 if ((part_id == TKP_TRACK) || (part_id == TKP_TRACKVERT)) {
1353 DrawEdge(hdc, &channel_rect, EDGE_SUNKEN, BF_RECT);
1354 } else if (part_id == TKP_THUMBVERT) {
1355 DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE);
1357 // Split rect into top and bottom pieces.
1358 RECT top_section = rect.ToRECT();
1359 RECT bottom_section = rect.ToRECT();
1360 top_section.bottom -= ((bottom_section.right - bottom_section.left) / 2);
1361 bottom_section.top = top_section.bottom;
1362 DrawEdge(hdc, &top_section, EDGE_RAISED,
1363 BF_LEFT | BF_TOP | BF_RIGHT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1365 // Split triangular piece into two diagonals.
1366 RECT& left_half = bottom_section;
1367 RECT right_half = bottom_section;
1368 right_half.left += ((bottom_section.right - bottom_section.left) / 2);
1369 left_half.right = right_half.left;
1370 DrawEdge(hdc, &left_half, EDGE_RAISED,
1371 BF_DIAGONAL_ENDTOPLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1372 DrawEdge(hdc, &right_half, EDGE_RAISED,
1373 BF_DIAGONAL_ENDBOTTOMLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1375 // If the button is pressed, draw hatching.
1376 if (extra.classic_state & DFCS_PUSHED) {
1378 SetCheckerboardShader(&paint, rect_win);
1380 // Fill all three pieces with the pattern.
1381 canvas->drawIRect(skia::RECTToSkIRect(top_section), paint);
1383 SkScalar left_triangle_top = SkIntToScalar(left_half.top);
1384 SkScalar left_triangle_right = SkIntToScalar(left_half.right);
1385 SkPath left_triangle;
1386 left_triangle.moveTo(SkIntToScalar(left_half.left), left_triangle_top);
1387 left_triangle.lineTo(left_triangle_right, left_triangle_top);
1388 left_triangle.lineTo(left_triangle_right,
1389 SkIntToScalar(left_half.bottom));
1390 left_triangle.close();
1391 canvas->drawPath(left_triangle, paint);
1393 SkScalar right_triangle_left = SkIntToScalar(right_half.left);
1394 SkScalar right_triangle_top = SkIntToScalar(right_half.top);
1395 SkPath right_triangle;
1396 right_triangle.moveTo(right_triangle_left, right_triangle_top);
1397 right_triangle.lineTo(SkIntToScalar(right_half.right),
1398 right_triangle_top);
1399 right_triangle.lineTo(right_triangle_left,
1400 SkIntToScalar(right_half.bottom));
1401 right_triangle.close();
1402 canvas->drawPath(right_triangle, paint);
1408 HRESULT NativeThemeWin::PaintProgressBar(
1410 const gfx::Rect& rect,
1411 const ProgressBarExtraParams& extra) const {
1412 // There is no documentation about the animation speed, frame-rate, nor
1413 // size of moving overlay of the indeterminate progress bar.
1414 // So we just observed real-world programs and guessed following parameters.
1415 const int kDeteminateOverlayPixelsPerSecond = 300;
1416 const int kDeteminateOverlayWidth = 120;
1417 const int kIndeterminateOverlayPixelsPerSecond = 175;
1418 const int kVistaIndeterminateOverlayWidth = 120;
1419 const int kXPIndeterminateOverlayWidth = 55;
1420 // The thickness of the bar frame inside |value_rect|
1421 const int kXPBarPadding = 3;
1423 RECT bar_rect = rect.ToRECT();
1424 RECT value_rect = gfx::Rect(extra.value_rect_x,
1426 extra.value_rect_width,
1427 extra.value_rect_height).ToRECT();
1429 bool pre_vista = base::win::GetVersion() < base::win::VERSION_VISTA;
1430 HANDLE handle = GetThemeHandle(PROGRESS);
1431 if (handle && draw_theme_ && draw_theme_ex_) {
1432 draw_theme_(handle, hdc, PP_BAR, 0, &bar_rect, NULL);
1434 int bar_width = bar_rect.right - bar_rect.left;
1435 if (extra.determinate) {
1436 // TODO(morrita): this RTL guess can be wrong.
1437 // We should pass the direction from WebKit side.
1438 bool is_rtl = (bar_rect.right == value_rect.right &&
1439 bar_rect.left != value_rect.left);
1440 // We should care the direction here because PP_CNUNK painting
1442 DTBGOPTS value_draw_options;
1443 value_draw_options.dwSize = sizeof(DTBGOPTS);
1444 value_draw_options.dwFlags = is_rtl ? DTBG_MIRRORDC : 0;
1445 value_draw_options.rcClip = bar_rect;
1448 // On XP, progress bar is chunk-style and has no glossy effect.
1449 // We need to shrink destination rect to fit the part inside the bar
1450 // with an appropriate margin.
1451 RECT shrunk_value_rect = InsetRect(&value_rect, kXPBarPadding);
1452 draw_theme_ex_(handle, hdc, PP_CHUNK, 0,
1453 &shrunk_value_rect, &value_draw_options);
1455 // On Vista or later, the progress bar part has a
1456 // single-block value part. It also has glossy effect.
1457 // And the value part has exactly same height as the bar part
1458 // so we don't need to shrink the rect.
1459 draw_theme_ex_(handle, hdc, PP_FILL, 0,
1460 &value_rect, &value_draw_options);
1462 int dx = ComputeAnimationProgress(bar_width,
1463 kDeteminateOverlayWidth,
1464 kDeteminateOverlayPixelsPerSecond,
1465 extra.animated_seconds);
1466 RECT overlay_rect = value_rect;
1467 overlay_rect.left += dx;
1468 overlay_rect.right = overlay_rect.left + kDeteminateOverlayWidth;
1469 draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &value_rect);
1472 // A glossy overlay for indeterminate progress bar has small pause
1473 // after each animation. We emulate this by adding an invisible margin
1474 // the animation has to traverse.
1475 int width_with_margin = bar_width + kIndeterminateOverlayPixelsPerSecond;
1476 int overlay_width = pre_vista ?
1477 kXPIndeterminateOverlayWidth : kVistaIndeterminateOverlayWidth;
1478 int dx = ComputeAnimationProgress(width_with_margin,
1480 kIndeterminateOverlayPixelsPerSecond,
1481 extra.animated_seconds);
1482 RECT overlay_rect = bar_rect;
1483 overlay_rect.left += dx;
1484 overlay_rect.right = overlay_rect.left + overlay_width;
1486 RECT shrunk_rect = InsetRect(&overlay_rect, kXPBarPadding);
1487 RECT shrunk_bar_rect = InsetRect(&bar_rect, kXPBarPadding);
1488 draw_theme_(handle, hdc, PP_CHUNK, 0, &shrunk_rect, &shrunk_bar_rect);
1490 draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &bar_rect);
1497 HBRUSH bg_brush = GetSysColorBrush(COLOR_BTNFACE);
1498 HBRUSH fg_brush = GetSysColorBrush(COLOR_BTNSHADOW);
1499 FillRect(hdc, &bar_rect, bg_brush);
1500 FillRect(hdc, &value_rect, fg_brush);
1501 DrawEdge(hdc, &bar_rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1505 HRESULT NativeThemeWin::PaintWindowResizeGripper(HDC hdc,
1506 const gfx::Rect& rect) const {
1507 HANDLE handle = GetThemeHandle(STATUS);
1508 RECT rect_win = rect.ToRECT();
1509 if (handle && draw_theme_) {
1510 // Paint the status bar gripper. There doesn't seem to be a
1511 // standard gripper in Windows for the space between
1512 // scrollbars. This is pretty close, but it's supposed to be
1513 // painted over a status bar.
1514 return draw_theme_(handle, hdc, SP_GRIPPER, 0, &rect_win, NULL);
1517 // Draw a windows classic scrollbar gripper.
1518 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
1522 HRESULT NativeThemeWin::PaintTabPanelBackground(HDC hdc,
1523 const gfx::Rect& rect) const {
1524 HANDLE handle = GetThemeHandle(TAB);
1525 RECT rect_win = rect.ToRECT();
1526 if (handle && draw_theme_)
1527 return draw_theme_(handle, hdc, TABP_BODY, 0, &rect_win, NULL);
1529 // Classic just renders a flat color background.
1530 FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
1534 HRESULT NativeThemeWin::PaintTextField(
1538 const gfx::Rect& rect,
1539 const TextFieldExtraParams& extra) const {
1540 int part_id = EP_EDITTEXT;
1541 int state_id = ETS_NORMAL;
1544 if (extra.is_read_only) {
1545 state_id = ETS_READONLY;
1546 } else if (extra.is_focused) {
1547 state_id = ETS_FOCUSED;
1549 state_id = ETS_NORMAL;
1556 state_id = ETS_SELECTED;
1559 state_id = ETS_DISABLED;
1562 NOTREACHED() << "Invalid state: " << state;
1566 RECT rect_win = rect.ToRECT();
1567 return PaintTextField(hdc, part_id, state_id, extra.classic_state,
1569 skia::SkColorToCOLORREF(extra.background_color),
1570 extra.fill_content_area, extra.draw_edges);
1573 HRESULT NativeThemeWin::PaintTextField(HDC hdc,
1579 bool fill_content_area,
1580 bool draw_edges) const {
1581 // TODO(ojan): http://b/1210017 Figure out how to give the ability to
1582 // exclude individual edges from being drawn.
1584 HANDLE handle = GetThemeHandle(TEXTFIELD);
1585 // TODO(mpcomplete): can we detect if the color is specified by the user,
1586 // and if not, just use the system color?
1587 // CreateSolidBrush() accepts a RGB value but alpha must be 0.
1588 HBRUSH bg_brush = CreateSolidBrush(color);
1590 // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible
1591 // draw_theme_ex_ is NULL and draw_theme_ is non-null.
1592 if (handle && (draw_theme_ex_ || (draw_theme_ && draw_edges))) {
1593 if (draw_theme_ex_) {
1594 static const DTBGOPTS omit_border_options = {
1599 const DTBGOPTS* draw_opts = draw_edges ? NULL : &omit_border_options;
1600 hr = draw_theme_ex_(handle, hdc, part_id, state_id, rect, draw_opts);
1602 hr = draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
1605 // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
1606 if (fill_content_area && get_theme_content_rect_) {
1608 hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect,
1610 FillRect(hdc, &content_rect, bg_brush);
1613 // Draw it manually.
1615 DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1617 if (fill_content_area) {
1618 FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ?
1619 reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1) : bg_brush);
1623 DeleteObject(bg_brush);
1627 HRESULT NativeThemeWin::PaintScaledTheme(HANDLE theme,
1631 const gfx::Rect& rect) const {
1632 // Correct the scaling and positioning of sub-components such as scrollbar
1633 // arrows and thumb grippers in the event that the world transform applies
1634 // scaling (e.g. in high-DPI mode).
1635 XFORM save_transform;
1636 if (GetWorldTransform(hdc, &save_transform)) {
1637 float scale = save_transform.eM11;
1638 if (scale != 1 && save_transform.eM12 == 0) {
1639 ModifyWorldTransform(hdc, NULL, MWT_IDENTITY);
1640 gfx::Rect scaled_rect = gfx::ToEnclosedRect(
1641 gfx::ScaleRect(rect, scale));
1642 RECT bounds = gfx::Rect(scaled_rect.x() + save_transform.eDx,
1643 scaled_rect.y() + save_transform.eDy,
1644 scaled_rect.width(),
1645 scaled_rect.height()).ToRECT();
1646 HRESULT result = draw_theme_(theme, hdc, part_id, state_id, &bounds,
1648 SetWorldTransform(hdc, &save_transform);
1652 RECT bounds = rect.ToRECT();
1653 return draw_theme_(theme, hdc, part_id, state_id, &bounds, NULL);
1657 NativeThemeWin::ThemeName NativeThemeWin::GetThemeName(Part part) {
1665 case kInnerSpinButton:
1669 case kMenuPopupGutter:
1671 case kMenuPopupArrow:
1672 case kMenuPopupSeparator:
1678 case kScrollbarDownArrow:
1679 case kScrollbarLeftArrow:
1680 case kScrollbarRightArrow:
1681 case kScrollbarUpArrow:
1682 case kScrollbarHorizontalThumb:
1683 case kScrollbarVerticalThumb:
1684 case kScrollbarHorizontalTrack:
1685 case kScrollbarVerticalTrack:
1695 case kWindowResizeGripper:
1699 NOTREACHED() << "Invalid part: " << part;
1706 int NativeThemeWin::GetWindowsPart(Part part,
1708 const ExtraParams& extra) {
1712 part_id = BP_CHECKBOX;
1715 part_id = MENU_POPUPCHECK;
1717 case kMenuPopupArrow:
1718 part_id = MENU_POPUPSUBMENU;
1720 case kMenuPopupGutter:
1721 part_id = MENU_POPUPGUTTER;
1723 case kMenuPopupSeparator:
1724 part_id = MENU_POPUPSEPARATOR;
1727 part_id = BP_PUSHBUTTON;
1730 part_id = BP_RADIOBUTTON;
1732 case kWindowResizeGripper:
1733 part_id = SP_GRIPPER;
1735 case kScrollbarDownArrow:
1736 case kScrollbarLeftArrow:
1737 case kScrollbarRightArrow:
1738 case kScrollbarUpArrow:
1739 part_id = SBP_ARROWBTN;
1741 case kScrollbarHorizontalThumb:
1742 part_id = SBP_THUMBBTNHORZ;
1744 case kScrollbarVerticalThumb:
1745 part_id = SBP_THUMBBTNVERT;
1748 NOTREACHED() << "Invalid part: " << part;
1754 int NativeThemeWin::GetWindowsState(Part part,
1756 const ExtraParams& extra) {
1762 state_id = CBS_UNCHECKEDNORMAL;
1765 state_id = CBS_UNCHECKEDHOT;
1768 state_id = CBS_UNCHECKEDPRESSED;
1771 state_id = CBS_UNCHECKEDDISABLED;
1774 NOTREACHED() << "Invalid state: " << state;
1783 state_id = extra.menu_check.is_radio ? MC_BULLETNORMAL
1784 : MC_CHECKMARKNORMAL;
1787 state_id = extra.menu_check.is_radio ? MC_BULLETDISABLED
1788 : MC_CHECKMARKDISABLED;
1791 NOTREACHED() << "Invalid state: " << state;
1795 case kMenuPopupArrow:
1796 case kMenuPopupGutter:
1797 case kMenuPopupSeparator:
1800 state_id = MBI_NORMAL;
1806 state_id = MBI_PUSHED;
1809 state_id = MBI_DISABLED;
1812 NOTREACHED() << "Invalid state: " << state;
1819 state_id = PBS_NORMAL;
1825 state_id = PBS_PRESSED;
1828 state_id = PBS_DISABLED;
1831 NOTREACHED() << "Invalid state: " << state;
1838 state_id = RBS_UNCHECKEDNORMAL;
1841 state_id = RBS_UNCHECKEDHOT;
1844 state_id = RBS_UNCHECKEDPRESSED;
1847 state_id = RBS_UNCHECKEDDISABLED;
1850 NOTREACHED() << "Invalid state: " << state;
1854 case kWindowResizeGripper:
1860 state_id = 1; // gripper has no windows state
1863 NOTREACHED() << "Invalid state: " << state;
1867 case kScrollbarDownArrow:
1870 state_id = ABS_DOWNNORMAL;
1873 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1874 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1875 ABS_DOWNHOT : ABS_DOWNHOVER;
1878 state_id = ABS_DOWNPRESSED;
1881 state_id = ABS_DOWNDISABLED;
1884 NOTREACHED() << "Invalid state: " << state;
1888 case kScrollbarLeftArrow:
1891 state_id = ABS_LEFTNORMAL;
1894 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1895 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1896 ABS_LEFTHOT : ABS_LEFTHOVER;
1899 state_id = ABS_LEFTPRESSED;
1902 state_id = ABS_LEFTDISABLED;
1905 NOTREACHED() << "Invalid state: " << state;
1909 case kScrollbarRightArrow:
1912 state_id = ABS_RIGHTNORMAL;
1915 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1916 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1917 ABS_RIGHTHOT : ABS_RIGHTHOVER;
1920 state_id = ABS_RIGHTPRESSED;
1923 state_id = ABS_RIGHTDISABLED;
1926 NOTREACHED() << "Invalid state: " << state;
1930 case kScrollbarUpArrow:
1933 state_id = ABS_UPNORMAL;
1936 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1937 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1938 ABS_UPHOT : ABS_UPHOVER;
1941 state_id = ABS_UPPRESSED;
1944 state_id = ABS_UPDISABLED;
1947 NOTREACHED() << "Invalid state: " << state;
1951 case kScrollbarHorizontalThumb:
1952 case kScrollbarVerticalThumb:
1955 state_id = SCRBS_NORMAL;
1958 // Mimic WebKit's behaviour in ScrollbarThemeChromiumWin.cpp.
1959 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1960 SCRBS_HOT : SCRBS_HOVER;
1963 state_id = SCRBS_PRESSED;
1966 state_id = SCRBS_DISABLED;
1969 NOTREACHED() << "Invalid state: " << state;
1974 NOTREACHED() << "Invalid part: " << part;
1980 HRESULT NativeThemeWin::GetThemeInt(ThemeName theme,
1985 HANDLE handle = GetThemeHandle(theme);
1986 if (handle && get_theme_int_)
1987 return get_theme_int_(handle, part_id, state_id, prop_id, value);
1991 HRESULT NativeThemeWin::PaintFrameControl(HDC hdc,
1992 const gfx::Rect& rect,
1996 State control_state) const {
1997 const int width = rect.width();
1998 const int height = rect.height();
2000 // DrawFrameControl for menu arrow/check wants a monochrome bitmap.
2001 base::win::ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL));
2003 if (mask_bitmap == NULL)
2004 return E_OUTOFMEMORY;
2006 base::win::ScopedCreateDC bitmap_dc(CreateCompatibleDC(NULL));
2007 base::win::ScopedSelectObject select_bitmap(bitmap_dc, mask_bitmap);
2008 RECT local_rect = { 0, 0, width, height };
2009 DrawFrameControl(bitmap_dc, &local_rect, type, state);
2011 // We're going to use BitBlt with a b&w mask. This results in using the dest
2012 // dc's text color for the black bits in the mask, and the dest dc's
2013 // background color for the white bits in the mask. DrawFrameControl draws the
2014 // check in black, and the background in white.
2017 switch (control_state) {
2018 case NativeTheme::kHovered:
2019 bg_color_key = COLOR_HIGHLIGHT;
2020 text_color_key = COLOR_HIGHLIGHTTEXT;
2022 case NativeTheme::kNormal:
2023 bg_color_key = COLOR_MENU;
2024 text_color_key = COLOR_MENUTEXT;
2026 case NativeTheme::kDisabled:
2027 bg_color_key = is_selected ? COLOR_HIGHLIGHT : COLOR_MENU;
2028 text_color_key = COLOR_GRAYTEXT;
2032 bg_color_key = COLOR_MENU;
2033 text_color_key = COLOR_MENUTEXT;
2036 COLORREF old_bg_color = SetBkColor(hdc, GetSysColor(bg_color_key));
2037 COLORREF old_text_color = SetTextColor(hdc, GetSysColor(text_color_key));
2038 BitBlt(hdc, rect.x(), rect.y(), width, height, bitmap_dc, 0, 0, SRCCOPY);
2039 SetBkColor(hdc, old_bg_color);
2040 SetTextColor(hdc, old_text_color);
2045 HANDLE NativeThemeWin::GetThemeHandle(ThemeName theme_name) const {
2046 if (!open_theme_ || theme_name < 0 || theme_name >= LAST)
2049 if (theme_handles_[theme_name])
2050 return theme_handles_[theme_name];
2052 // Not found, try to load it.
2054 switch (theme_name) {
2056 handle = open_theme_(NULL, L"Button");
2059 handle = open_theme_(NULL, L"Listview");
2062 handle = open_theme_(NULL, L"Menu");
2065 handle = open_theme_(NULL, L"Combobox");
2068 handle = open_theme_(NULL, L"Scrollbar");
2071 handle = open_theme_(NULL, L"Status");
2074 handle = open_theme_(NULL, L"Tab");
2077 handle = open_theme_(NULL, L"Edit");
2080 handle = open_theme_(NULL, L"Trackbar");
2083 handle = open_theme_(NULL, L"Window");
2086 handle = open_theme_(NULL, L"Progress");
2089 handle = open_theme_(NULL, L"Spin");
2094 theme_handles_[theme_name] = handle;