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/win/dpi.h"
31 #include "ui/native_theme/common_theme.h"
33 // This was removed from Winvers.h but is still used.
34 #if !defined(COLOR_MENUHIGHLIGHT)
35 #define COLOR_MENUHIGHLIGHT 29
40 // TODO: Obtain the correct colors using GetSysColor.
41 // Theme colors returned by GetSystemColor().
42 const SkColor kInvalidColorIdColor = SkColorSetRGB(255, 0, 128);
44 const SkColor kDialogBackgroundColor = SkColorSetRGB(251, 251, 251);
46 const SkColor kFocusedBorderColor = SkColorSetRGB(0x4d, 0x90, 0xfe);
47 const SkColor kUnfocusedBorderColor = SkColorSetRGB(0xd9, 0xd9, 0xd9);
49 const SkColor kButtonBackgroundColor = SkColorSetRGB(0xde, 0xde, 0xde);
50 const SkColor kButtonHighlightColor = SkColorSetARGB(200, 255, 255, 255);
51 const SkColor kButtonHoverColor = SkColorSetRGB(6, 45, 117);
52 const SkColor kButtonHoverBackgroundColor = SkColorSetRGB(0xEA, 0xEA, 0xEA);
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);
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();
134 bool NativeThemeWin::IsThemingActive() const {
135 if (is_theme_active_)
136 return !!is_theme_active_();
140 bool NativeThemeWin::IsUsingHighContrastTheme() const {
141 if (is_using_high_contrast_valid_)
142 return is_using_high_contrast_;
144 result.cbSize = sizeof(HIGHCONTRAST);
145 is_using_high_contrast_ =
146 SystemParametersInfo(SPI_GETHIGHCONTRAST, result.cbSize, &result, 0) &&
147 (result.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON;
148 is_using_high_contrast_valid_ = true;
149 return is_using_high_contrast_;
152 HRESULT NativeThemeWin::GetThemeColor(ThemeName theme,
156 SkColor* color) const {
157 HANDLE handle = GetThemeHandle(theme);
158 if (handle && get_theme_color_) {
160 if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) ==
162 *color = skia::COLORREFToSkColor(color_ref);
169 SkColor NativeThemeWin::GetThemeColorWithDefault(ThemeName theme,
173 int default_sys_color) const {
175 if (GetThemeColor(theme, part_id, state_id, prop_id, &color) != S_OK)
176 color = color_utils::GetSysSkColor(default_sys_color);
180 gfx::Size NativeThemeWin::GetThemeBorderSize(ThemeName theme) const {
181 // For simplicity use the wildcard state==0, part==0, since it works
182 // for the cases we currently depend on.
184 if (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK)
185 return gfx::Size(border, border);
187 return gfx::Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE));
190 void NativeThemeWin::DisableTheming() const {
191 if (!set_theme_properties_)
193 set_theme_properties_(0);
196 void NativeThemeWin::CloseHandles() const {
200 for (int i = 0; i < LAST; ++i) {
201 if (theme_handles_[i]) {
202 close_theme_(theme_handles_[i]);
203 theme_handles_[i] = NULL;
208 bool NativeThemeWin::IsClassicTheme(ThemeName name) const {
212 return !GetThemeHandle(name);
216 NativeThemeWin* NativeThemeWin::instance() {
217 CR_DEFINE_STATIC_LOCAL(NativeThemeWin, s_native_theme, ());
218 return &s_native_theme;
221 gfx::Size NativeThemeWin::GetPartSize(Part part,
223 const ExtraParams& extra) const {
224 gfx::Size part_size = CommonThemeGetPartSize(part, state, extra);
225 if (!part_size.IsEmpty())
228 // The GetThemePartSize call below returns the default size without
229 // accounting for user customization (crbug/218291).
231 case kScrollbarDownArrow:
232 case kScrollbarLeftArrow:
233 case kScrollbarRightArrow:
234 case kScrollbarUpArrow:
235 case kScrollbarHorizontalThumb:
236 case kScrollbarVerticalThumb:
237 case kScrollbarHorizontalTrack:
238 case kScrollbarVerticalTrack: {
239 int size = gfx::win::GetSystemMetricsInDIP(SM_CXVSCROLL);
242 return gfx::Size(size, size);
246 int part_id = GetWindowsPart(part, state, extra);
247 int state_id = GetWindowsState(part, state, extra);
250 HDC hdc = GetDC(NULL);
251 HRESULT hr = GetThemePartSize(GetThemeName(part), hdc, part_id, state_id,
252 NULL, TS_TRUE, &size);
253 ReleaseDC(NULL, hdc);
256 // TODO(rogerta): For now, we need to support radio buttons and checkboxes
257 // when theming is not enabled. Support for other parts can be added
262 // TODO(rogerta): I was not able to find any API to get the default
263 // size of these controls, so determined these values empirically.
274 return gfx::Size(size.cx, size.cy);
277 void NativeThemeWin::Paint(SkCanvas* canvas,
280 const gfx::Rect& rect,
281 const ExtraParams& extra) const {
286 case kMenuPopupGutter:
287 CommonThemePaintMenuGutter(canvas, rect);
289 case kMenuPopupSeparator:
290 CommonThemePaintMenuSeparator(canvas, rect, extra.menu_separator);
292 case kMenuPopupBackground:
293 CommonThemePaintMenuBackground(canvas, rect);
295 case kMenuItemBackground:
296 CommonThemePaintMenuItemBackground(canvas, state, rect);
300 bool needs_paint_indirect = false;
301 if (!skia::SupportsPlatformPaint(canvas)) {
302 // This block will only get hit with --enable-accelerated-drawing flag.
303 needs_paint_indirect = true;
305 // Scrollbar components on Windows Classic theme (on all Windows versions)
306 // have particularly problematic alpha values, so always draw them
307 // indirectly. In addition, scrollbar thumbs and grippers for the Windows XP
308 // theme (available only on Windows XP) also need their alpha values
311 case kScrollbarDownArrow:
312 case kScrollbarUpArrow:
313 case kScrollbarLeftArrow:
314 case kScrollbarRightArrow:
315 if (!GetThemeHandle(SCROLLBAR))
316 needs_paint_indirect = true;
318 case kScrollbarHorizontalThumb:
319 case kScrollbarVerticalThumb:
320 case kScrollbarHorizontalGripper:
321 case kScrollbarVerticalGripper:
322 if (!GetThemeHandle(SCROLLBAR) ||
323 base::win::GetVersion() == base::win::VERSION_XP)
324 needs_paint_indirect = true;
331 if (needs_paint_indirect)
332 PaintIndirect(canvas, part, state, rect, extra);
334 PaintDirect(canvas, part, state, rect, extra);
337 NativeThemeWin::NativeThemeWin()
338 : theme_dll_(LoadLibrary(L"uxtheme.dll")),
340 draw_theme_ex_(NULL),
341 get_theme_color_(NULL),
342 get_theme_content_rect_(NULL),
343 get_theme_part_size_(NULL),
346 set_theme_properties_(NULL),
347 is_theme_active_(NULL),
348 get_theme_int_(NULL),
349 color_change_listener_(this),
350 is_using_high_contrast_(false),
351 is_using_high_contrast_valid_(false) {
353 draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>(
354 GetProcAddress(theme_dll_, "DrawThemeBackground"));
355 draw_theme_ex_ = reinterpret_cast<DrawThemeBackgroundExPtr>(
356 GetProcAddress(theme_dll_, "DrawThemeBackgroundEx"));
357 get_theme_color_ = reinterpret_cast<GetThemeColorPtr>(
358 GetProcAddress(theme_dll_, "GetThemeColor"));
359 get_theme_content_rect_ = reinterpret_cast<GetThemeContentRectPtr>(
360 GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect"));
361 get_theme_part_size_ = reinterpret_cast<GetThemePartSizePtr>(
362 GetProcAddress(theme_dll_, "GetThemePartSize"));
363 open_theme_ = reinterpret_cast<OpenThemeDataPtr>(
364 GetProcAddress(theme_dll_, "OpenThemeData"));
365 close_theme_ = reinterpret_cast<CloseThemeDataPtr>(
366 GetProcAddress(theme_dll_, "CloseThemeData"));
367 set_theme_properties_ = reinterpret_cast<SetThemeAppPropertiesPtr>(
368 GetProcAddress(theme_dll_, "SetThemeAppProperties"));
369 is_theme_active_ = reinterpret_cast<IsThemeActivePtr>(
370 GetProcAddress(theme_dll_, "IsThemeActive"));
371 get_theme_int_ = reinterpret_cast<GetThemeIntPtr>(
372 GetProcAddress(theme_dll_, "GetThemeInt"));
374 memset(theme_handles_, 0, sizeof(theme_handles_));
376 // Initialize the cached system colors.
377 UpdateSystemColors();
380 NativeThemeWin::~NativeThemeWin() {
382 // todo (cpu): fix this soon. Making a call to CloseHandles() here breaks
383 // certain tests and the reliability bots.
385 FreeLibrary(theme_dll_);
389 void NativeThemeWin::OnSysColorChange() {
390 UpdateSystemColors();
391 is_using_high_contrast_valid_ = false;
395 void NativeThemeWin::UpdateSystemColors() {
396 for (int i = 0; i < arraysize(kSystemColors); ++i) {
397 system_colors_[kSystemColors[i]] =
398 color_utils::GetSysSkColor(kSystemColors[i]);
402 void NativeThemeWin::PaintDirect(SkCanvas* canvas,
405 const gfx::Rect& rect,
406 const ExtraParams& extra) const {
407 skia::ScopedPlatformPaint scoped_platform_paint(canvas);
408 HDC hdc = scoped_platform_paint.GetPlatformSurface();
412 PaintCheckbox(hdc, part, state, rect, extra.button);
415 PaintRadioButton(hdc, part, state, rect, extra.button);
418 PaintPushButton(hdc, part, state, rect, extra.button);
420 case kMenuPopupArrow:
421 PaintMenuArrow(hdc, state, rect, extra.menu_arrow);
423 case kMenuPopupGutter:
424 PaintMenuGutter(hdc, rect);
426 case kMenuPopupSeparator:
427 PaintMenuSeparator(hdc, rect, extra.menu_separator);
429 case kMenuPopupBackground:
430 PaintMenuBackground(hdc, rect);
433 PaintMenuCheck(hdc, state, rect, extra.menu_check);
435 case kMenuCheckBackground:
436 PaintMenuCheckBackground(hdc, state, rect);
438 case kMenuItemBackground:
439 PaintMenuItemBackground(hdc, state, rect, extra.menu_item);
442 PaintMenuList(hdc, state, rect, extra.menu_list);
444 case kScrollbarDownArrow:
445 case kScrollbarUpArrow:
446 case kScrollbarLeftArrow:
447 case kScrollbarRightArrow:
448 PaintScrollbarArrow(hdc, part, state, rect, extra.scrollbar_arrow);
450 case kScrollbarHorizontalTrack:
451 case kScrollbarVerticalTrack:
452 PaintScrollbarTrack(canvas, hdc, part, state, rect,
453 extra.scrollbar_track);
455 case kScrollbarCorner:
456 canvas->drawColor(SK_ColorWHITE, SkXfermode::kSrc_Mode);
458 case kScrollbarHorizontalThumb:
459 case kScrollbarVerticalThumb:
460 case kScrollbarHorizontalGripper:
461 case kScrollbarVerticalGripper:
462 PaintScrollbarThumb(hdc, part, state, rect, extra.scrollbar_thumb);
464 case kInnerSpinButton:
465 PaintSpinButton(hdc, part, state, rect, extra.inner_spin);
469 PaintTrackbar(canvas, hdc, part, state, rect, extra.trackbar);
472 PaintProgressBar(hdc, rect, extra.progress_bar);
474 case kWindowResizeGripper:
475 PaintWindowResizeGripper(hdc, rect);
477 case kTabPanelBackground:
478 PaintTabPanelBackground(hdc, rect);
481 PaintTextField(hdc, part, state, rect, extra.text_field);
487 // While transitioning NativeThemeWin to the single Paint() entry point,
488 // unsupported parts will DCHECK here.
493 SkColor NativeThemeWin::GetSystemColor(ColorId color_id) const {
495 if (CommonThemeGetSystemColor(color_id, &color))
500 case kColorId_WindowBackground:
501 return system_colors_[COLOR_WINDOW];
504 case kColorId_DialogBackground:
505 if (gfx::IsInvertedColorScheme())
506 return color_utils::InvertColor(kDialogBackgroundColor);
507 return kDialogBackgroundColor;
510 case kColorId_FocusedBorderColor:
511 return kFocusedBorderColor;
512 case kColorId_UnfocusedBorderColor:
513 return kUnfocusedBorderColor;
516 case kColorId_ButtonBackgroundColor:
517 return kButtonBackgroundColor;
518 case kColorId_ButtonEnabledColor:
519 return system_colors_[COLOR_BTNTEXT];
520 case kColorId_ButtonDisabledColor:
521 return system_colors_[COLOR_GRAYTEXT];
522 case kColorId_ButtonHighlightColor:
523 return kButtonHighlightColor;
524 case kColorId_ButtonHoverColor:
525 return kButtonHoverColor;
526 case kColorId_ButtonHoverBackgroundColor:
527 return kButtonHoverBackgroundColor;
530 case kColorId_EnabledMenuItemForegroundColor:
531 return kEnabledMenuItemForegroundColor;
532 case kColorId_DisabledMenuItemForegroundColor:
533 return kDisabledMenuItemForegroundColor;
534 case kColorId_DisabledEmphasizedMenuItemForegroundColor:
535 return SK_ColorBLACK;
536 case kColorId_FocusedMenuItemBackgroundColor:
537 return kFocusedMenuItemBackgroundColor;
538 case kColorId_MenuSeparatorColor:
539 return kMenuSeparatorColor;
542 case kColorId_LabelEnabledColor:
543 return system_colors_[COLOR_BTNTEXT];
544 case kColorId_LabelDisabledColor:
545 return system_colors_[COLOR_GRAYTEXT];
546 case kColorId_LabelBackgroundColor:
547 return system_colors_[COLOR_WINDOW];
550 case kColorId_TextfieldDefaultColor:
551 return system_colors_[COLOR_WINDOWTEXT];
552 case kColorId_TextfieldDefaultBackground:
553 return system_colors_[COLOR_WINDOW];
554 case kColorId_TextfieldReadOnlyColor:
555 return system_colors_[COLOR_GRAYTEXT];
556 case kColorId_TextfieldReadOnlyBackground:
557 return system_colors_[COLOR_3DFACE];
558 case kColorId_TextfieldSelectionColor:
559 return system_colors_[COLOR_HIGHLIGHTTEXT];
560 case kColorId_TextfieldSelectionBackgroundFocused:
561 return system_colors_[COLOR_HIGHLIGHT];
564 // NOTE: these aren't right for all themes, but as close as I could get.
565 case kColorId_TreeBackground:
566 return system_colors_[COLOR_WINDOW];
567 case kColorId_TreeText:
568 return system_colors_[COLOR_WINDOWTEXT];
569 case kColorId_TreeSelectedText:
570 return system_colors_[COLOR_HIGHLIGHTTEXT];
571 case kColorId_TreeSelectedTextUnfocused:
572 return system_colors_[COLOR_BTNTEXT];
573 case kColorId_TreeSelectionBackgroundFocused:
574 return system_colors_[COLOR_HIGHLIGHT];
575 case kColorId_TreeSelectionBackgroundUnfocused:
576 return system_colors_[IsUsingHighContrastTheme() ?
577 COLOR_MENUHIGHLIGHT : COLOR_BTNFACE];
578 case kColorId_TreeArrow:
579 return system_colors_[COLOR_WINDOWTEXT];
582 case kColorId_TableBackground:
583 return system_colors_[COLOR_WINDOW];
584 case kColorId_TableText:
585 return system_colors_[COLOR_WINDOWTEXT];
586 case kColorId_TableSelectedText:
587 return system_colors_[COLOR_HIGHLIGHTTEXT];
588 case kColorId_TableSelectedTextUnfocused:
589 return system_colors_[COLOR_BTNTEXT];
590 case kColorId_TableSelectionBackgroundFocused:
591 return system_colors_[COLOR_HIGHLIGHT];
592 case kColorId_TableSelectionBackgroundUnfocused:
593 return system_colors_[IsUsingHighContrastTheme() ?
594 COLOR_MENUHIGHLIGHT : COLOR_BTNFACE];
595 case kColorId_TableGroupingIndicatorColor:
596 return system_colors_[COLOR_GRAYTEXT];
599 case kColorId_ResultsTableNormalBackground:
600 return system_colors_[COLOR_WINDOW];
601 case kColorId_ResultsTableHoveredBackground:
602 return color_utils::AlphaBlend(system_colors_[COLOR_HIGHLIGHT],
603 system_colors_[COLOR_WINDOW], 0x40);
604 case kColorId_ResultsTableSelectedBackground:
605 return system_colors_[COLOR_HIGHLIGHT];
606 case kColorId_ResultsTableNormalText:
607 case kColorId_ResultsTableHoveredText:
608 return system_colors_[COLOR_WINDOWTEXT];
609 case kColorId_ResultsTableSelectedText:
610 return system_colors_[COLOR_HIGHLIGHTTEXT];
611 case kColorId_ResultsTableNormalDimmedText:
612 return color_utils::AlphaBlend(system_colors_[COLOR_WINDOWTEXT],
613 system_colors_[COLOR_WINDOW], 0x80);
614 case kColorId_ResultsTableHoveredDimmedText:
615 return color_utils::AlphaBlend(
616 system_colors_[COLOR_WINDOWTEXT],
617 GetSystemColor(kColorId_ResultsTableHoveredBackground), 0x80);
618 case kColorId_ResultsTableSelectedDimmedText:
619 return color_utils::AlphaBlend(system_colors_[COLOR_HIGHLIGHTTEXT],
620 system_colors_[COLOR_HIGHLIGHT], 0x80);
621 case kColorId_ResultsTableNormalUrl:
622 return color_utils::GetReadableColor(SkColorSetRGB(0, 128, 0),
623 system_colors_[COLOR_WINDOW]);
624 case kColorId_ResultsTableHoveredUrl:
625 return color_utils::GetReadableColor(
626 SkColorSetRGB(0, 128, 0),
627 GetSystemColor(kColorId_ResultsTableHoveredBackground));
628 case kColorId_ResultsTableSelectedUrl:
629 return color_utils::GetReadableColor(SkColorSetRGB(0, 128, 0),
630 system_colors_[COLOR_HIGHLIGHT]);
631 case kColorId_ResultsTableNormalDivider:
632 return color_utils::AlphaBlend(system_colors_[COLOR_WINDOWTEXT],
633 system_colors_[COLOR_WINDOW], 0x34);
634 case kColorId_ResultsTableHoveredDivider:
635 return color_utils::AlphaBlend(
636 system_colors_[COLOR_WINDOWTEXT],
637 GetSystemColor(kColorId_ResultsTableHoveredBackground), 0x34);
638 case kColorId_ResultsTableSelectedDivider:
639 return color_utils::AlphaBlend(system_colors_[COLOR_HIGHLIGHTTEXT],
640 system_colors_[COLOR_HIGHLIGHT], 0x34);
646 return kInvalidColorIdColor;
649 void NativeThemeWin::PaintIndirect(SkCanvas* canvas,
652 const gfx::Rect& rect,
653 const ExtraParams& extra) const {
654 // TODO(asvitkine): This path is pretty inefficient - for each paint operation
655 // it creates a new offscreen bitmap Skia canvas. This can
656 // be sped up by doing it only once per part/state and
657 // keeping a cache of the resulting bitmaps.
659 // Create an offscreen canvas that is backed by an HDC.
660 skia::RefPtr<skia::BitmapPlatformDevice> device = skia::AdoptRef(
661 skia::BitmapPlatformDevice::Create(
662 rect.width(), rect.height(), false, NULL));
666 SkCanvas offscreen_canvas(device.get());
667 DCHECK(skia::SupportsPlatformPaint(&offscreen_canvas));
669 // Some of the Windows theme drawing operations do not write correct alpha
670 // values for fully-opaque pixels; instead the pixels get alpha 0. This is
671 // especially a problem on Windows XP or when using the Classic theme.
673 // To work-around this, mark all pixels with a placeholder value, to detect
674 // which pixels get touched by the paint operation. After paint, set any
675 // pixels that have alpha 0 to opaque and placeholders to fully-transparent.
676 const SkColor placeholder = SkColorSetARGB(1, 0, 0, 0);
677 offscreen_canvas.clear(placeholder);
679 // Offset destination rects to have origin (0,0).
680 gfx::Rect adjusted_rect(rect.size());
681 ExtraParams adjusted_extra(extra);
684 adjusted_extra.progress_bar.value_rect_x = 0;
685 adjusted_extra.progress_bar.value_rect_y = 0;
687 case kScrollbarHorizontalTrack:
688 case kScrollbarVerticalTrack:
689 adjusted_extra.scrollbar_track.track_x = 0;
690 adjusted_extra.scrollbar_track.track_y = 0;
694 // Draw the theme controls using existing HDC-drawing code.
695 PaintDirect(&offscreen_canvas,
701 // Copy the pixels to a bitmap that has ref-counted pixel storage, which is
702 // necessary to have when drawing to a SkPicture.
703 const SkBitmap& hdc_bitmap =
704 offscreen_canvas.getDevice()->accessBitmap(false);
706 hdc_bitmap.copyTo(&bitmap, kPMColor_SkColorType);
708 // Post-process the pixels to fix up the alpha values (see big comment above).
709 const SkPMColor placeholder_value = SkPreMultiplyColor(placeholder);
710 const int pixel_count = rect.width() * rect.height();
711 SkPMColor* pixels = bitmap.getAddr32(0, 0);
712 for (int i = 0; i < pixel_count; i++) {
713 if (pixels[i] == placeholder_value) {
714 // Pixel wasn't touched - make it fully transparent.
715 pixels[i] = SkPackARGB32(0, 0, 0, 0);
716 } else if (SkGetPackedA32(pixels[i]) == 0) {
717 // Pixel was touched but has incorrect alpha of 0, make it fully opaque.
718 pixels[i] = SkPackARGB32(0xFF,
719 SkGetPackedR32(pixels[i]),
720 SkGetPackedG32(pixels[i]),
721 SkGetPackedB32(pixels[i]));
725 // Draw the offscreen bitmap to the destination canvas.
726 canvas->drawBitmap(bitmap, rect.x(), rect.y());
729 HRESULT NativeThemeWin::GetThemePartSize(ThemeName theme_name,
736 HANDLE handle = GetThemeHandle(theme_name);
737 if (handle && get_theme_part_size_)
738 return get_theme_part_size_(handle, hdc, part_id, state_id, rect, ts, size);
743 HRESULT NativeThemeWin::PaintButton(HDC hdc,
745 const ButtonExtraParams& extra,
749 HANDLE handle = GetThemeHandle(BUTTON);
750 if (handle && draw_theme_)
751 return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
753 // Adjust classic_state based on part, state, and extras.
754 int classic_state = extra.classic_state;
757 classic_state |= DFCS_BUTTONCHECK;
760 classic_state |= DFCS_BUTTONRADIO;
763 classic_state |= DFCS_BUTTONPUSH;
766 NOTREACHED() << "Unknown part_id: " << part_id;
772 classic_state |= DFCS_INACTIVE;
775 classic_state |= DFCS_PUSHED;
781 NOTREACHED() << "Unknown state: " << state;
786 classic_state |= DFCS_CHECKED;
789 // All pressed states have both low bits set, and no other states do.
790 const bool focused = ((state_id & ETS_FOCUSED) == ETS_FOCUSED);
791 const bool pressed = ((state_id & PBS_PRESSED) == PBS_PRESSED);
792 if ((BP_PUSHBUTTON == part_id) && (pressed || focused)) {
793 // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the
794 // button itself is shrunk by 1 pixel.
795 HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW);
797 FrameRect(hdc, rect, brush);
798 InflateRect(rect, -1, -1);
801 DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state);
803 // Draw the focus rectangle (the dotted line box) only on buttons. For radio
804 // and checkboxes, we let webkit draw the focus rectangle (orange glow).
805 if ((BP_PUSHBUTTON == part_id) && focused) {
806 // The focus rect is inside the button. The exact number of pixels depends
807 // on whether we're in classic mode or using uxtheme.
808 if (handle && get_theme_content_rect_) {
809 get_theme_content_rect_(handle, hdc, part_id, state_id, rect, rect);
811 InflateRect(rect, -GetSystemMetrics(SM_CXEDGE),
812 -GetSystemMetrics(SM_CYEDGE));
814 DrawFocusRect(hdc, rect);
817 // Classic theme doesn't support indeterminate checkboxes. We draw
818 // a recangle inside a checkbox like IE10 does.
819 if (part_id == BP_CHECKBOX && extra.indeterminate) {
820 RECT inner_rect = *rect;
821 // "4 / 13" is same as IE10 in classic theme.
822 int padding = (inner_rect.right - inner_rect.left) * 4 / 13;
823 InflateRect(&inner_rect, -padding, -padding);
824 int color_index = state == kDisabled ? COLOR_GRAYTEXT : COLOR_WINDOWTEXT;
825 FillRect(hdc, &inner_rect, GetSysColorBrush(color_index));
831 HRESULT NativeThemeWin::PaintMenuSeparator(
833 const gfx::Rect& rect,
834 const MenuSeparatorExtraParams& extra) const {
835 RECT rect_win = rect.ToRECT();
837 HANDLE handle = GetThemeHandle(MENU);
838 if (handle && draw_theme_) {
839 // Delta is needed for non-classic to move separator up slightly.
842 return draw_theme_(handle, hdc, MENU_POPUPSEPARATOR, MPI_NORMAL, &rect_win,
846 DrawEdge(hdc, &rect_win, EDGE_ETCHED, BF_TOP);
850 HRESULT NativeThemeWin::PaintMenuGutter(HDC hdc,
851 const gfx::Rect& rect) const {
852 RECT rect_win = rect.ToRECT();
853 HANDLE handle = GetThemeHandle(MENU);
854 if (handle && draw_theme_)
855 return draw_theme_(handle, hdc, MENU_POPUPGUTTER, MPI_NORMAL, &rect_win,
860 HRESULT NativeThemeWin::PaintMenuArrow(HDC hdc,
862 const gfx::Rect& rect,
863 const MenuArrowExtraParams& extra)
865 int state_id = MSM_NORMAL;
866 if (state == kDisabled)
867 state_id = MSM_DISABLED;
869 HANDLE handle = GetThemeHandle(MENU);
870 RECT rect_win = rect.ToRECT();
871 if (handle && draw_theme_) {
872 if (extra.pointing_right) {
873 return draw_theme_(handle, hdc, MENU_POPUPSUBMENU, state_id, &rect_win,
876 // There is no way to tell the uxtheme API to draw a left pointing arrow;
877 // it doesn't have a flag equivalent to DFCS_MENUARROWRIGHT. But they
878 // are needed for RTL locales on Vista. So use a memory DC and mirror
879 // the region with GDI's StretchBlt.
881 base::win::ScopedCreateDC mem_dc(CreateCompatibleDC(hdc));
882 base::win::ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(),
884 base::win::ScopedSelectObject select_bitmap(mem_dc, mem_bitmap);
885 // Copy and horizontally mirror the background from hdc into mem_dc. Use
886 // a negative-width source rect, starting at the rightmost pixel.
887 StretchBlt(mem_dc, 0, 0, r.width(), r.height(),
888 hdc, r.right()-1, r.y(), -r.width(), r.height(), SRCCOPY);
890 RECT theme_rect = {0, 0, r.width(), r.height()};
891 HRESULT result = draw_theme_(handle, mem_dc, MENU_POPUPSUBMENU,
892 state_id, &theme_rect, NULL);
893 // Copy and mirror the result back into mem_dc.
894 StretchBlt(hdc, r.x(), r.y(), r.width(), r.height(),
895 mem_dc, r.width()-1, 0, -r.width(), r.height(), SRCCOPY);
900 // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
901 // left pointing arrow. This makes the following 'if' statement slightly
904 if (extra.pointing_right)
905 pfc_state = DFCS_MENUARROW;
907 pfc_state = DFCS_MENUARROWRIGHT;
908 return PaintFrameControl(hdc, rect, DFC_MENU, pfc_state, extra.is_selected,
912 HRESULT NativeThemeWin::PaintMenuBackground(HDC hdc,
913 const gfx::Rect& rect) const {
914 HANDLE handle = GetThemeHandle(MENU);
915 RECT rect_win = rect.ToRECT();
916 if (handle && draw_theme_) {
917 HRESULT result = draw_theme_(handle, hdc, MENU_POPUPBACKGROUND, 0,
919 FrameRect(hdc, &rect_win, GetSysColorBrush(COLOR_3DSHADOW));
923 FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_MENU));
924 DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT);
928 HRESULT NativeThemeWin::PaintMenuCheck(
931 const gfx::Rect& rect,
932 const MenuCheckExtraParams& extra) const {
933 HANDLE handle = GetThemeHandle(MENU);
935 if (extra.is_radio) {
936 state_id = state == kDisabled ? MC_BULLETDISABLED : MC_BULLETNORMAL;
938 state_id = state == kDisabled ? MC_CHECKMARKDISABLED : MC_CHECKMARKNORMAL;
941 RECT rect_win = rect.ToRECT();
942 if (handle && draw_theme_)
943 return draw_theme_(handle, hdc, MENU_POPUPCHECK, state_id, &rect_win, NULL);
945 return PaintFrameControl(hdc, rect, DFC_MENU,
946 extra.is_radio ? DFCS_MENUBULLET : DFCS_MENUCHECK,
947 extra.is_selected, state);
950 HRESULT NativeThemeWin::PaintMenuCheckBackground(HDC hdc,
952 const gfx::Rect& rect) const {
953 HANDLE handle = GetThemeHandle(MENU);
954 int state_id = state == kDisabled ? MCB_DISABLED : MCB_NORMAL;
955 RECT rect_win = rect.ToRECT();
956 if (handle && draw_theme_)
957 return draw_theme_(handle, hdc, MENU_POPUPCHECKBACKGROUND, state_id,
959 // Nothing to do for background.
963 HRESULT NativeThemeWin::PaintMenuItemBackground(
966 const gfx::Rect& rect,
967 const MenuItemExtraParams& extra) const {
968 HANDLE handle = GetThemeHandle(MENU);
969 RECT rect_win = rect.ToRECT();
973 state_id = MPI_NORMAL;
976 state_id = extra.is_selected ? MPI_DISABLEDHOT : MPI_DISABLED;
982 NOTREACHED() << "Invalid state " << state;
986 if (handle && draw_theme_)
987 return draw_theme_(handle, hdc, MENU_POPUPITEM, state_id, &rect_win, NULL);
989 if (extra.is_selected)
990 FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_HIGHLIGHT));
994 HRESULT NativeThemeWin::PaintPushButton(HDC hdc,
997 const gfx::Rect& rect,
998 const ButtonExtraParams& extra) const {
1002 state_id = PBS_DISABLED;
1008 state_id = extra.is_default ? PBS_DEFAULTED : PBS_NORMAL;
1011 state_id = PBS_PRESSED;
1014 NOTREACHED() << "Invalid state: " << state;
1018 RECT rect_win = rect.ToRECT();
1019 return PaintButton(hdc, state, extra, BP_PUSHBUTTON, state_id, &rect_win);
1022 HRESULT NativeThemeWin::PaintRadioButton(HDC hdc,
1025 const gfx::Rect& rect,
1026 const ButtonExtraParams& extra) const {
1030 state_id = extra.checked ? RBS_CHECKEDDISABLED : RBS_UNCHECKEDDISABLED;
1033 state_id = extra.checked ? RBS_CHECKEDHOT : RBS_UNCHECKEDHOT;
1036 state_id = extra.checked ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL;
1039 state_id = extra.checked ? RBS_CHECKEDPRESSED : RBS_UNCHECKEDPRESSED;
1042 NOTREACHED() << "Invalid state: " << state;
1046 RECT rect_win = rect.ToRECT();
1047 return PaintButton(hdc, state, extra, BP_RADIOBUTTON, state_id, &rect_win);
1050 HRESULT NativeThemeWin::PaintCheckbox(HDC hdc,
1053 const gfx::Rect& rect,
1054 const ButtonExtraParams& extra) const {
1058 state_id = extra.checked ? CBS_CHECKEDDISABLED :
1059 extra.indeterminate ? CBS_MIXEDDISABLED :
1060 CBS_UNCHECKEDDISABLED;
1063 state_id = extra.checked ? CBS_CHECKEDHOT :
1064 extra.indeterminate ? CBS_MIXEDHOT :
1068 state_id = extra.checked ? CBS_CHECKEDNORMAL :
1069 extra.indeterminate ? CBS_MIXEDNORMAL :
1070 CBS_UNCHECKEDNORMAL;
1073 state_id = extra.checked ? CBS_CHECKEDPRESSED :
1074 extra.indeterminate ? CBS_MIXEDPRESSED :
1075 CBS_UNCHECKEDPRESSED;
1078 NOTREACHED() << "Invalid state: " << state;
1082 RECT rect_win = rect.ToRECT();
1083 return PaintButton(hdc, state, extra, BP_CHECKBOX, state_id, &rect_win);
1086 HRESULT NativeThemeWin::PaintMenuList(HDC hdc,
1088 const gfx::Rect& rect,
1089 const MenuListExtraParams& extra) const {
1090 HANDLE handle = GetThemeHandle(MENULIST);
1091 RECT rect_win = rect.ToRECT();
1095 state_id = CBXS_NORMAL;
1098 state_id = CBXS_DISABLED;
1101 state_id = CBXS_HOT;
1104 state_id = CBXS_PRESSED;
1107 NOTREACHED() << "Invalid state " << state;
1111 if (handle && draw_theme_)
1112 return draw_theme_(handle, hdc, CP_DROPDOWNBUTTON, state_id, &rect_win,
1115 // Draw it manually.
1116 DrawFrameControl(hdc, &rect_win, DFC_SCROLL,
1117 DFCS_SCROLLCOMBOBOX | extra.classic_state);
1121 HRESULT NativeThemeWin::PaintScrollbarArrow(
1125 const gfx::Rect& rect,
1126 const ScrollbarArrowExtraParams& extra) const {
1127 static const int state_id_matrix[4][kMaxState] = {
1128 ABS_DOWNDISABLED, ABS_DOWNHOT, ABS_DOWNNORMAL, ABS_DOWNPRESSED,
1129 ABS_LEFTDISABLED, ABS_LEFTHOT, ABS_LEFTNORMAL, ABS_LEFTPRESSED,
1130 ABS_RIGHTDISABLED, ABS_RIGHTHOT, ABS_RIGHTNORMAL, ABS_RIGHTPRESSED,
1131 ABS_UPDISABLED, ABS_UPHOT, ABS_UPNORMAL, ABS_UPPRESSED
1133 HANDLE handle = GetThemeHandle(SCROLLBAR);
1134 RECT rect_win = rect.ToRECT();
1135 if (handle && draw_theme_) {
1136 int index = part - kScrollbarDownArrow;
1137 DCHECK(index >=0 && index < 4);
1138 int state_id = state_id_matrix[index][state];
1140 // Hovering means that the cursor is over the scroolbar, but not over the
1141 // specific arrow itself. We don't want to show it "hot" mode, but only
1143 if (state == kHovered && extra.is_hovering) {
1145 case kScrollbarDownArrow:
1146 state_id = ABS_DOWNHOVER;
1148 case kScrollbarLeftArrow:
1149 state_id = ABS_LEFTHOVER;
1151 case kScrollbarRightArrow:
1152 state_id = ABS_RIGHTHOVER;
1154 case kScrollbarUpArrow:
1155 state_id = ABS_UPHOVER;
1158 NOTREACHED() << "Invalid part: " << part;
1162 return PaintScaledTheme(handle, hdc, SBP_ARROWBTN, state_id, rect);
1165 int classic_state = DFCS_SCROLLDOWN;
1167 case kScrollbarDownArrow:
1168 classic_state = DFCS_SCROLLDOWN;
1170 case kScrollbarLeftArrow:
1171 classic_state = DFCS_SCROLLLEFT;
1173 case kScrollbarRightArrow:
1174 classic_state = DFCS_SCROLLRIGHT;
1176 case kScrollbarUpArrow:
1177 classic_state = DFCS_SCROLLUP;
1180 NOTREACHED() << "Invalid part: " << part;
1185 classic_state |= DFCS_INACTIVE;
1188 classic_state |= DFCS_HOT;
1193 classic_state |= DFCS_PUSHED;
1196 NOTREACHED() << "Invalid state: " << state;
1199 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, classic_state);
1203 HRESULT NativeThemeWin::PaintScrollbarThumb(
1207 const gfx::Rect& rect,
1208 const ScrollbarThumbExtraParams& extra) const {
1209 HANDLE handle = GetThemeHandle(SCROLLBAR);
1210 RECT rect_win = rect.ToRECT();
1215 case NativeTheme::kScrollbarHorizontalThumb:
1216 part_id = SBP_THUMBBTNHORZ;
1218 case NativeTheme::kScrollbarVerticalThumb:
1219 part_id = SBP_THUMBBTNVERT;
1221 case NativeTheme::kScrollbarHorizontalGripper:
1222 part_id = SBP_GRIPPERHORZ;
1224 case NativeTheme::kScrollbarVerticalGripper:
1225 part_id = SBP_GRIPPERVERT;
1228 NOTREACHED() << "Invalid part: " << part;
1234 state_id = SCRBS_DISABLED;
1237 state_id = extra.is_hovering ? SCRBS_HOVER : SCRBS_HOT;
1240 state_id = SCRBS_NORMAL;
1243 state_id = SCRBS_PRESSED;
1246 NOTREACHED() << "Invalid state: " << state;
1250 if (handle && draw_theme_)
1251 return PaintScaledTheme(handle, hdc, part_id, state_id, rect);
1253 // Draw it manually.
1254 if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT))
1255 DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_MIDDLE);
1256 // Classic mode doesn't have a gripper.
1260 HRESULT NativeThemeWin::PaintScrollbarTrack(
1265 const gfx::Rect& rect,
1266 const ScrollbarTrackExtraParams& extra) const {
1267 HANDLE handle = GetThemeHandle(SCROLLBAR);
1268 RECT rect_win = rect.ToRECT();
1273 case NativeTheme::kScrollbarHorizontalTrack:
1274 part_id = extra.is_upper ? SBP_UPPERTRACKHORZ : SBP_LOWERTRACKHORZ;
1276 case NativeTheme::kScrollbarVerticalTrack:
1277 part_id = extra.is_upper ? SBP_UPPERTRACKVERT : SBP_LOWERTRACKVERT;
1280 NOTREACHED() << "Invalid part: " << part;
1286 state_id = SCRBS_DISABLED;
1289 state_id = SCRBS_HOVER;
1292 state_id = SCRBS_NORMAL;
1295 state_id = SCRBS_PRESSED;
1298 NOTREACHED() << "Invalid state: " << state;
1302 if (handle && draw_theme_)
1303 return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
1305 // Draw it manually.
1306 if ((system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_3DFACE]) &&
1307 (system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_WINDOW])) {
1308 FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_SCROLLBAR + 1));
1311 RECT align_rect = gfx::Rect(extra.track_x, extra.track_y, extra.track_width,
1312 extra.track_height).ToRECT();
1313 SetCheckerboardShader(&paint, align_rect);
1314 canvas->drawIRect(skia::RECTToSkIRect(rect_win), paint);
1316 if (extra.classic_state & DFCS_PUSHED)
1317 InvertRect(hdc, &rect_win);
1321 HRESULT NativeThemeWin::PaintSpinButton(
1325 const gfx::Rect& rect,
1326 const InnerSpinButtonExtraParams& extra) const {
1327 HANDLE handle = GetThemeHandle(SPIN);
1328 RECT rect_win = rect.ToRECT();
1329 int part_id = extra.spin_up ? SPNP_UP : SPNP_DOWN;
1333 state_id = extra.spin_up ? UPS_DISABLED : DNS_DISABLED;
1336 state_id = extra.spin_up ? UPS_HOT : DNS_HOT;
1339 state_id = extra.spin_up ? UPS_NORMAL : DNS_NORMAL;
1342 state_id = extra.spin_up ? UPS_PRESSED : DNS_PRESSED;
1345 NOTREACHED() << "Invalid state " << state;
1349 if (handle && draw_theme_)
1350 return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
1351 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, extra.classic_state);
1355 HRESULT NativeThemeWin::PaintTrackbar(
1360 const gfx::Rect& rect,
1361 const TrackbarExtraParams& extra) const {
1362 int part_id = part == kTrackbarTrack ? TKP_TRACK : TKP_THUMBBOTTOM;
1364 part_id = part == kTrackbarTrack ? TKP_TRACKVERT : TKP_THUMBVERT;
1369 state_id = TUS_DISABLED;
1375 state_id = TUS_NORMAL;
1378 state_id = TUS_PRESSED;
1381 NOTREACHED() << "Invalid state " << state;
1385 // Make the channel be 4 px thick in the center of the supplied rect. (4 px
1386 // matches what XP does in various menus; GetThemePartSize() doesn't seem to
1387 // return good values here.)
1388 RECT rect_win = rect.ToRECT();
1389 RECT channel_rect = rect.ToRECT();
1390 const int channel_thickness = 4;
1391 if (part_id == TKP_TRACK) {
1393 ((channel_rect.bottom - channel_rect.top - channel_thickness) / 2);
1394 channel_rect.bottom = channel_rect.top + channel_thickness;
1395 } else if (part_id == TKP_TRACKVERT) {
1396 channel_rect.left +=
1397 ((channel_rect.right - channel_rect.left - channel_thickness) / 2);
1398 channel_rect.right = channel_rect.left + channel_thickness;
1399 } // else this isn't actually a channel, so |channel_rect| == |rect|.
1401 HANDLE handle = GetThemeHandle(TRACKBAR);
1402 if (handle && draw_theme_)
1403 return draw_theme_(handle, hdc, part_id, state_id, &channel_rect, NULL);
1405 // Classic mode, draw it manually.
1406 if ((part_id == TKP_TRACK) || (part_id == TKP_TRACKVERT)) {
1407 DrawEdge(hdc, &channel_rect, EDGE_SUNKEN, BF_RECT);
1408 } else if (part_id == TKP_THUMBVERT) {
1409 DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE);
1411 // Split rect into top and bottom pieces.
1412 RECT top_section = rect.ToRECT();
1413 RECT bottom_section = rect.ToRECT();
1414 top_section.bottom -= ((bottom_section.right - bottom_section.left) / 2);
1415 bottom_section.top = top_section.bottom;
1416 DrawEdge(hdc, &top_section, EDGE_RAISED,
1417 BF_LEFT | BF_TOP | BF_RIGHT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1419 // Split triangular piece into two diagonals.
1420 RECT& left_half = bottom_section;
1421 RECT right_half = bottom_section;
1422 right_half.left += ((bottom_section.right - bottom_section.left) / 2);
1423 left_half.right = right_half.left;
1424 DrawEdge(hdc, &left_half, EDGE_RAISED,
1425 BF_DIAGONAL_ENDTOPLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1426 DrawEdge(hdc, &right_half, EDGE_RAISED,
1427 BF_DIAGONAL_ENDBOTTOMLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1429 // If the button is pressed, draw hatching.
1430 if (extra.classic_state & DFCS_PUSHED) {
1432 SetCheckerboardShader(&paint, rect_win);
1434 // Fill all three pieces with the pattern.
1435 canvas->drawIRect(skia::RECTToSkIRect(top_section), paint);
1437 SkScalar left_triangle_top = SkIntToScalar(left_half.top);
1438 SkScalar left_triangle_right = SkIntToScalar(left_half.right);
1439 SkPath left_triangle;
1440 left_triangle.moveTo(SkIntToScalar(left_half.left), left_triangle_top);
1441 left_triangle.lineTo(left_triangle_right, left_triangle_top);
1442 left_triangle.lineTo(left_triangle_right,
1443 SkIntToScalar(left_half.bottom));
1444 left_triangle.close();
1445 canvas->drawPath(left_triangle, paint);
1447 SkScalar right_triangle_left = SkIntToScalar(right_half.left);
1448 SkScalar right_triangle_top = SkIntToScalar(right_half.top);
1449 SkPath right_triangle;
1450 right_triangle.moveTo(right_triangle_left, right_triangle_top);
1451 right_triangle.lineTo(SkIntToScalar(right_half.right),
1452 right_triangle_top);
1453 right_triangle.lineTo(right_triangle_left,
1454 SkIntToScalar(right_half.bottom));
1455 right_triangle.close();
1456 canvas->drawPath(right_triangle, paint);
1462 HRESULT NativeThemeWin::PaintProgressBar(
1464 const gfx::Rect& rect,
1465 const ProgressBarExtraParams& extra) const {
1466 // There is no documentation about the animation speed, frame-rate, nor
1467 // size of moving overlay of the indeterminate progress bar.
1468 // So we just observed real-world programs and guessed following parameters.
1469 const int kDeteminateOverlayPixelsPerSecond = 300;
1470 const int kDeteminateOverlayWidth = 120;
1471 const int kIndeterminateOverlayPixelsPerSecond = 175;
1472 const int kVistaIndeterminateOverlayWidth = 120;
1473 const int kXPIndeterminateOverlayWidth = 55;
1474 // The thickness of the bar frame inside |value_rect|
1475 const int kXPBarPadding = 3;
1477 RECT bar_rect = rect.ToRECT();
1478 RECT value_rect = gfx::Rect(extra.value_rect_x,
1480 extra.value_rect_width,
1481 extra.value_rect_height).ToRECT();
1483 bool pre_vista = base::win::GetVersion() < base::win::VERSION_VISTA;
1484 HANDLE handle = GetThemeHandle(PROGRESS);
1485 if (handle && draw_theme_ && draw_theme_ex_) {
1486 draw_theme_(handle, hdc, PP_BAR, 0, &bar_rect, NULL);
1488 int bar_width = bar_rect.right - bar_rect.left;
1489 if (extra.determinate) {
1490 // TODO(morrita): this RTL guess can be wrong.
1491 // We should pass the direction from WebKit side.
1492 bool is_rtl = (bar_rect.right == value_rect.right &&
1493 bar_rect.left != value_rect.left);
1494 // We should care the direction here because PP_CNUNK painting
1496 DTBGOPTS value_draw_options;
1497 value_draw_options.dwSize = sizeof(DTBGOPTS);
1498 value_draw_options.dwFlags = is_rtl ? DTBG_MIRRORDC : 0;
1499 value_draw_options.rcClip = bar_rect;
1502 // On XP, progress bar is chunk-style and has no glossy effect.
1503 // We need to shrink destination rect to fit the part inside the bar
1504 // with an appropriate margin.
1505 RECT shrunk_value_rect = InsetRect(&value_rect, kXPBarPadding);
1506 draw_theme_ex_(handle, hdc, PP_CHUNK, 0,
1507 &shrunk_value_rect, &value_draw_options);
1509 // On Vista or later, the progress bar part has a
1510 // single-block value part. It also has glossy effect.
1511 // And the value part has exactly same height as the bar part
1512 // so we don't need to shrink the rect.
1513 draw_theme_ex_(handle, hdc, PP_FILL, 0,
1514 &value_rect, &value_draw_options);
1516 int dx = ComputeAnimationProgress(bar_width,
1517 kDeteminateOverlayWidth,
1518 kDeteminateOverlayPixelsPerSecond,
1519 extra.animated_seconds);
1520 RECT overlay_rect = value_rect;
1521 overlay_rect.left += dx;
1522 overlay_rect.right = overlay_rect.left + kDeteminateOverlayWidth;
1523 draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &value_rect);
1526 // A glossy overlay for indeterminate progress bar has small pause
1527 // after each animation. We emulate this by adding an invisible margin
1528 // the animation has to traverse.
1529 int width_with_margin = bar_width + kIndeterminateOverlayPixelsPerSecond;
1530 int overlay_width = pre_vista ?
1531 kXPIndeterminateOverlayWidth : kVistaIndeterminateOverlayWidth;
1532 int dx = ComputeAnimationProgress(width_with_margin,
1534 kIndeterminateOverlayPixelsPerSecond,
1535 extra.animated_seconds);
1536 RECT overlay_rect = bar_rect;
1537 overlay_rect.left += dx;
1538 overlay_rect.right = overlay_rect.left + overlay_width;
1540 RECT shrunk_rect = InsetRect(&overlay_rect, kXPBarPadding);
1541 RECT shrunk_bar_rect = InsetRect(&bar_rect, kXPBarPadding);
1542 draw_theme_(handle, hdc, PP_CHUNK, 0, &shrunk_rect, &shrunk_bar_rect);
1544 draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &bar_rect);
1551 HBRUSH bg_brush = GetSysColorBrush(COLOR_BTNFACE);
1552 HBRUSH fg_brush = GetSysColorBrush(COLOR_BTNSHADOW);
1553 FillRect(hdc, &bar_rect, bg_brush);
1554 FillRect(hdc, &value_rect, fg_brush);
1555 DrawEdge(hdc, &bar_rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1559 HRESULT NativeThemeWin::PaintWindowResizeGripper(HDC hdc,
1560 const gfx::Rect& rect) const {
1561 HANDLE handle = GetThemeHandle(STATUS);
1562 RECT rect_win = rect.ToRECT();
1563 if (handle && draw_theme_) {
1564 // Paint the status bar gripper. There doesn't seem to be a
1565 // standard gripper in Windows for the space between
1566 // scrollbars. This is pretty close, but it's supposed to be
1567 // painted over a status bar.
1568 return draw_theme_(handle, hdc, SP_GRIPPER, 0, &rect_win, NULL);
1571 // Draw a windows classic scrollbar gripper.
1572 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
1576 HRESULT NativeThemeWin::PaintTabPanelBackground(HDC hdc,
1577 const gfx::Rect& rect) const {
1578 HANDLE handle = GetThemeHandle(TAB);
1579 RECT rect_win = rect.ToRECT();
1580 if (handle && draw_theme_)
1581 return draw_theme_(handle, hdc, TABP_BODY, 0, &rect_win, NULL);
1583 // Classic just renders a flat color background.
1584 FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
1588 HRESULT NativeThemeWin::PaintTextField(
1592 const gfx::Rect& rect,
1593 const TextFieldExtraParams& extra) const {
1594 int part_id = EP_EDITTEXT;
1595 int state_id = ETS_NORMAL;
1598 if (extra.is_read_only) {
1599 state_id = ETS_READONLY;
1600 } else if (extra.is_focused) {
1601 state_id = ETS_FOCUSED;
1603 state_id = ETS_NORMAL;
1610 state_id = ETS_SELECTED;
1613 state_id = ETS_DISABLED;
1616 NOTREACHED() << "Invalid state: " << state;
1620 RECT rect_win = rect.ToRECT();
1621 return PaintTextField(hdc, part_id, state_id, extra.classic_state,
1623 skia::SkColorToCOLORREF(extra.background_color),
1624 extra.fill_content_area, extra.draw_edges);
1627 HRESULT NativeThemeWin::PaintTextField(HDC hdc,
1633 bool fill_content_area,
1634 bool draw_edges) const {
1635 // TODO(ojan): http://b/1210017 Figure out how to give the ability to
1636 // exclude individual edges from being drawn.
1638 HANDLE handle = GetThemeHandle(TEXTFIELD);
1639 // TODO(mpcomplete): can we detect if the color is specified by the user,
1640 // and if not, just use the system color?
1641 // CreateSolidBrush() accepts a RGB value but alpha must be 0.
1642 HBRUSH bg_brush = CreateSolidBrush(color);
1644 // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible
1645 // draw_theme_ex_ is NULL and draw_theme_ is non-null.
1646 if (handle && (draw_theme_ex_ || (draw_theme_ && draw_edges))) {
1647 if (draw_theme_ex_) {
1648 static const DTBGOPTS omit_border_options = {
1653 const DTBGOPTS* draw_opts = draw_edges ? NULL : &omit_border_options;
1654 hr = draw_theme_ex_(handle, hdc, part_id, state_id, rect, draw_opts);
1656 hr = draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
1659 // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
1660 if (fill_content_area && get_theme_content_rect_) {
1662 hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect,
1664 FillRect(hdc, &content_rect, bg_brush);
1667 // Draw it manually.
1669 DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1671 if (fill_content_area) {
1672 FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ?
1673 reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1) : bg_brush);
1677 DeleteObject(bg_brush);
1681 HRESULT NativeThemeWin::PaintScaledTheme(HANDLE theme,
1685 const gfx::Rect& rect) const {
1686 // Correct the scaling and positioning of sub-components such as scrollbar
1687 // arrows and thumb grippers in the event that the world transform applies
1688 // scaling (e.g. in high-DPI mode).
1689 XFORM save_transform;
1690 if (GetWorldTransform(hdc, &save_transform)) {
1691 float scale = save_transform.eM11;
1692 if (scale != 1 && save_transform.eM12 == 0) {
1693 ModifyWorldTransform(hdc, NULL, MWT_IDENTITY);
1694 gfx::Rect scaled_rect = gfx::ToEnclosedRect(
1695 gfx::ScaleRect(rect, scale));
1696 RECT bounds = gfx::Rect(scaled_rect.x() + save_transform.eDx,
1697 scaled_rect.y() + save_transform.eDy,
1698 scaled_rect.width(),
1699 scaled_rect.height()).ToRECT();
1700 HRESULT result = draw_theme_(theme, hdc, part_id, state_id, &bounds,
1702 SetWorldTransform(hdc, &save_transform);
1706 RECT bounds = rect.ToRECT();
1707 return draw_theme_(theme, hdc, part_id, state_id, &bounds, NULL);
1711 NativeThemeWin::ThemeName NativeThemeWin::GetThemeName(Part part) {
1719 case kInnerSpinButton:
1723 case kMenuPopupGutter:
1725 case kMenuPopupArrow:
1726 case kMenuPopupSeparator:
1732 case kScrollbarDownArrow:
1733 case kScrollbarLeftArrow:
1734 case kScrollbarRightArrow:
1735 case kScrollbarUpArrow:
1736 case kScrollbarHorizontalThumb:
1737 case kScrollbarVerticalThumb:
1738 case kScrollbarHorizontalTrack:
1739 case kScrollbarVerticalTrack:
1749 case kWindowResizeGripper:
1753 NOTREACHED() << "Invalid part: " << part;
1760 int NativeThemeWin::GetWindowsPart(Part part,
1762 const ExtraParams& extra) {
1766 part_id = BP_CHECKBOX;
1769 part_id = MENU_POPUPCHECK;
1771 case kMenuPopupArrow:
1772 part_id = MENU_POPUPSUBMENU;
1774 case kMenuPopupGutter:
1775 part_id = MENU_POPUPGUTTER;
1777 case kMenuPopupSeparator:
1778 part_id = MENU_POPUPSEPARATOR;
1781 part_id = BP_PUSHBUTTON;
1784 part_id = BP_RADIOBUTTON;
1786 case kWindowResizeGripper:
1787 part_id = SP_GRIPPER;
1789 case kScrollbarDownArrow:
1790 case kScrollbarLeftArrow:
1791 case kScrollbarRightArrow:
1792 case kScrollbarUpArrow:
1793 part_id = SBP_ARROWBTN;
1795 case kScrollbarHorizontalThumb:
1796 part_id = SBP_THUMBBTNHORZ;
1798 case kScrollbarVerticalThumb:
1799 part_id = SBP_THUMBBTNVERT;
1802 NOTREACHED() << "Invalid part: " << part;
1808 int NativeThemeWin::GetWindowsState(Part part,
1810 const ExtraParams& extra) {
1816 state_id = CBS_UNCHECKEDNORMAL;
1819 state_id = CBS_UNCHECKEDHOT;
1822 state_id = CBS_UNCHECKEDPRESSED;
1825 state_id = CBS_UNCHECKEDDISABLED;
1828 NOTREACHED() << "Invalid state: " << state;
1837 state_id = extra.menu_check.is_radio ? MC_BULLETNORMAL
1838 : MC_CHECKMARKNORMAL;
1841 state_id = extra.menu_check.is_radio ? MC_BULLETDISABLED
1842 : MC_CHECKMARKDISABLED;
1845 NOTREACHED() << "Invalid state: " << state;
1849 case kMenuPopupArrow:
1850 case kMenuPopupGutter:
1851 case kMenuPopupSeparator:
1854 state_id = MBI_NORMAL;
1860 state_id = MBI_PUSHED;
1863 state_id = MBI_DISABLED;
1866 NOTREACHED() << "Invalid state: " << state;
1873 state_id = PBS_NORMAL;
1879 state_id = PBS_PRESSED;
1882 state_id = PBS_DISABLED;
1885 NOTREACHED() << "Invalid state: " << state;
1892 state_id = RBS_UNCHECKEDNORMAL;
1895 state_id = RBS_UNCHECKEDHOT;
1898 state_id = RBS_UNCHECKEDPRESSED;
1901 state_id = RBS_UNCHECKEDDISABLED;
1904 NOTREACHED() << "Invalid state: " << state;
1908 case kWindowResizeGripper:
1914 state_id = 1; // gripper has no windows state
1917 NOTREACHED() << "Invalid state: " << state;
1921 case kScrollbarDownArrow:
1924 state_id = ABS_DOWNNORMAL;
1927 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1928 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1929 ABS_DOWNHOT : ABS_DOWNHOVER;
1932 state_id = ABS_DOWNPRESSED;
1935 state_id = ABS_DOWNDISABLED;
1938 NOTREACHED() << "Invalid state: " << state;
1942 case kScrollbarLeftArrow:
1945 state_id = ABS_LEFTNORMAL;
1948 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1949 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1950 ABS_LEFTHOT : ABS_LEFTHOVER;
1953 state_id = ABS_LEFTPRESSED;
1956 state_id = ABS_LEFTDISABLED;
1959 NOTREACHED() << "Invalid state: " << state;
1963 case kScrollbarRightArrow:
1966 state_id = ABS_RIGHTNORMAL;
1969 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1970 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1971 ABS_RIGHTHOT : ABS_RIGHTHOVER;
1974 state_id = ABS_RIGHTPRESSED;
1977 state_id = ABS_RIGHTDISABLED;
1980 NOTREACHED() << "Invalid state: " << state;
1984 case kScrollbarUpArrow:
1987 state_id = ABS_UPNORMAL;
1990 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1991 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1992 ABS_UPHOT : ABS_UPHOVER;
1995 state_id = ABS_UPPRESSED;
1998 state_id = ABS_UPDISABLED;
2001 NOTREACHED() << "Invalid state: " << state;
2005 case kScrollbarHorizontalThumb:
2006 case kScrollbarVerticalThumb:
2009 state_id = SCRBS_NORMAL;
2012 // Mimic WebKit's behaviour in ScrollbarThemeChromiumWin.cpp.
2013 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
2014 SCRBS_HOT : SCRBS_HOVER;
2017 state_id = SCRBS_PRESSED;
2020 state_id = SCRBS_DISABLED;
2023 NOTREACHED() << "Invalid state: " << state;
2028 NOTREACHED() << "Invalid part: " << part;
2034 HRESULT NativeThemeWin::GetThemeInt(ThemeName theme,
2039 HANDLE handle = GetThemeHandle(theme);
2040 if (handle && get_theme_int_)
2041 return get_theme_int_(handle, part_id, state_id, prop_id, value);
2045 HRESULT NativeThemeWin::PaintFrameControl(HDC hdc,
2046 const gfx::Rect& rect,
2050 State control_state) const {
2051 const int width = rect.width();
2052 const int height = rect.height();
2054 // DrawFrameControl for menu arrow/check wants a monochrome bitmap.
2055 base::win::ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL));
2057 if (mask_bitmap == NULL)
2058 return E_OUTOFMEMORY;
2060 base::win::ScopedCreateDC bitmap_dc(CreateCompatibleDC(NULL));
2061 base::win::ScopedSelectObject select_bitmap(bitmap_dc, mask_bitmap);
2062 RECT local_rect = { 0, 0, width, height };
2063 DrawFrameControl(bitmap_dc, &local_rect, type, state);
2065 // We're going to use BitBlt with a b&w mask. This results in using the dest
2066 // dc's text color for the black bits in the mask, and the dest dc's
2067 // background color for the white bits in the mask. DrawFrameControl draws the
2068 // check in black, and the background in white.
2071 switch (control_state) {
2072 case NativeTheme::kHovered:
2073 bg_color_key = COLOR_HIGHLIGHT;
2074 text_color_key = COLOR_HIGHLIGHTTEXT;
2076 case NativeTheme::kNormal:
2077 bg_color_key = COLOR_MENU;
2078 text_color_key = COLOR_MENUTEXT;
2080 case NativeTheme::kDisabled:
2081 bg_color_key = is_selected ? COLOR_HIGHLIGHT : COLOR_MENU;
2082 text_color_key = COLOR_GRAYTEXT;
2086 bg_color_key = COLOR_MENU;
2087 text_color_key = COLOR_MENUTEXT;
2090 COLORREF old_bg_color = SetBkColor(hdc, GetSysColor(bg_color_key));
2091 COLORREF old_text_color = SetTextColor(hdc, GetSysColor(text_color_key));
2092 BitBlt(hdc, rect.x(), rect.y(), width, height, bitmap_dc, 0, 0, SRCCOPY);
2093 SetBkColor(hdc, old_bg_color);
2094 SetTextColor(hdc, old_text_color);
2099 HANDLE NativeThemeWin::GetThemeHandle(ThemeName theme_name) const {
2100 if (!open_theme_ || theme_name < 0 || theme_name >= LAST)
2103 if (theme_handles_[theme_name])
2104 return theme_handles_[theme_name];
2106 // Not found, try to load it.
2108 switch (theme_name) {
2110 handle = open_theme_(NULL, L"Button");
2113 handle = open_theme_(NULL, L"Listview");
2116 handle = open_theme_(NULL, L"Menu");
2119 handle = open_theme_(NULL, L"Combobox");
2122 handle = open_theme_(NULL, L"Scrollbar");
2125 handle = open_theme_(NULL, L"Status");
2128 handle = open_theme_(NULL, L"Tab");
2131 handle = open_theme_(NULL, L"Edit");
2134 handle = open_theme_(NULL, L"Trackbar");
2137 handle = open_theme_(NULL, L"Window");
2140 handle = open_theme_(NULL, L"Progress");
2143 handle = open_theme_(NULL, L"Spin");
2148 theme_handles_[theme_name] = handle;