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_ptr.h"
15 #include "base/win/scoped_gdi_object.h"
16 #include "base/win/scoped_hdc.h"
17 #include "base/win/scoped_select_object.h"
18 #include "base/win/windows_version.h"
19 #include "skia/ext/bitmap_platform_device.h"
20 #include "skia/ext/platform_canvas.h"
21 #include "skia/ext/skia_utils_win.h"
22 #include "third_party/skia/include/core/SkCanvas.h"
23 #include "third_party/skia/include/core/SkColorPriv.h"
24 #include "third_party/skia/include/core/SkShader.h"
25 #include "ui/gfx/color_utils.h"
26 #include "ui/gfx/gdi_util.h"
27 #include "ui/gfx/rect.h"
28 #include "ui/gfx/rect_conversions.h"
29 #include "ui/gfx/win/dpi.h"
30 #include "ui/native_theme/common_theme.h"
32 // This was removed from Winvers.h but is still used.
33 #if !defined(COLOR_MENUHIGHLIGHT)
34 #define COLOR_MENUHIGHLIGHT 29
39 // TODO: Obtain the correct colors using GetSysColor.
40 // Theme colors returned by GetSystemColor().
41 const SkColor kInvalidColorIdColor
= SkColorSetRGB(255, 0, 128);
43 const SkColor kDialogBackgroundColor
= SkColorSetRGB(251, 251, 251);
45 const SkColor kFocusedBorderColor
= SkColorSetRGB(0x4d, 0x90, 0xfe);
46 const SkColor kUnfocusedBorderColor
= SkColorSetRGB(0xd9, 0xd9, 0xd9);
48 const SkColor kButtonBackgroundColor
= SkColorSetRGB(0xde, 0xde, 0xde);
49 const SkColor kButtonHighlightColor
= SkColorSetARGB(200, 255, 255, 255);
50 const SkColor kButtonHoverColor
= SkColorSetRGB(6, 45, 117);
51 const SkColor kButtonHoverBackgroundColor
= SkColorSetRGB(0xEA, 0xEA, 0xEA);
53 const SkColor kEnabledMenuItemForegroundColor
= SkColorSetRGB(6, 45, 117);
54 const SkColor kDisabledMenuItemForegroundColor
= SkColorSetRGB(161, 161, 146);
55 const SkColor kFocusedMenuItemBackgroundColor
= SkColorSetRGB(246, 249, 253);
56 const SkColor kMenuSeparatorColor
= SkColorSetARGB(50, 0, 0, 0);
58 const SkColor kTreeSelectionBackgroundUnfocused
= SkColorSetRGB(240, 240, 240);
60 // Windows system color IDs cached and updated by the native theme.
61 const int kSystemColors
[] = {
74 void SetCheckerboardShader(SkPaint
* paint
, const RECT
& align_rect
) {
75 // Create a 2x2 checkerboard pattern using the 3D face and highlight colors.
76 const SkColor face
= color_utils::GetSysSkColor(COLOR_3DFACE
);
77 const SkColor highlight
= color_utils::GetSysSkColor(COLOR_3DHILIGHT
);
78 SkColor buffer
[] = { face
, highlight
, highlight
, face
};
79 // Confusing bit: we first create a temporary bitmap with our desired pattern,
80 // then copy it to another bitmap. The temporary bitmap doesn't take
81 // ownership of the pixel data, and so will point to garbage when this
82 // function returns. The copy will copy the pixel data into a place owned by
83 // the bitmap, which is in turn owned by the shader, etc., so it will live
84 // until we're done using it.
85 SkImageInfo info
= SkImageInfo::MakeN32Premul(2, 2);
87 temp_bitmap
.installPixels(info
, buffer
, info
.minRowBytes());
89 temp_bitmap
.copyTo(&bitmap
);
91 // Align the pattern with the upper corner of |align_rect|.
92 SkMatrix local_matrix
;
93 local_matrix
.setTranslate(SkIntToScalar(align_rect
.left
),
94 SkIntToScalar(align_rect
.top
));
95 skia::RefPtr
<SkShader
> shader
=
96 skia::AdoptRef(SkShader::CreateBitmapShader(bitmap
,
97 SkShader::kRepeat_TileMode
,
98 SkShader::kRepeat_TileMode
,
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 return is_theme_active_
&& is_theme_active_();
138 bool NativeThemeWin::IsUsingHighContrastTheme() const {
139 if (is_using_high_contrast_valid_
)
140 return is_using_high_contrast_
;
142 result
.cbSize
= sizeof(HIGHCONTRAST
);
143 is_using_high_contrast_
=
144 SystemParametersInfo(SPI_GETHIGHCONTRAST
, result
.cbSize
, &result
, 0) &&
145 (result
.dwFlags
& HCF_HIGHCONTRASTON
) == HCF_HIGHCONTRASTON
;
146 is_using_high_contrast_valid_
= true;
147 return is_using_high_contrast_
;
150 HRESULT
NativeThemeWin::GetThemeColor(ThemeName theme
,
154 SkColor
* color
) const {
155 HANDLE handle
= GetThemeHandle(theme
);
156 if (!handle
|| !get_theme_color_
)
159 if (get_theme_color_(handle
, part_id
, state_id
, prop_id
, &color_ref
) != S_OK
)
161 *color
= skia::COLORREFToSkColor(color_ref
);
165 SkColor
NativeThemeWin::GetThemeColorWithDefault(ThemeName theme
,
169 int default_sys_color
) const {
171 return (GetThemeColor(theme
, part_id
, state_id
, prop_id
, &color
) == S_OK
) ?
172 color
: color_utils::GetSysSkColor(default_sys_color
);
175 gfx::Size
NativeThemeWin::GetThemeBorderSize(ThemeName theme
) const {
176 // For simplicity use the wildcard state==0, part==0, since it works
177 // for the cases we currently depend on.
179 return (GetThemeInt(theme
, 0, 0, TMT_BORDERSIZE
, &border
) == S_OK
) ?
180 gfx::Size(border
, border
) :
181 gfx::Size(GetSystemMetrics(SM_CXEDGE
), GetSystemMetrics(SM_CYEDGE
));
184 void NativeThemeWin::DisableTheming() const {
185 if (set_theme_properties_
)
186 set_theme_properties_(0);
189 void NativeThemeWin::CloseHandles() const {
193 for (int i
= 0; i
< LAST
; ++i
) {
194 if (theme_handles_
[i
]) {
195 close_theme_(theme_handles_
[i
]);
196 theme_handles_
[i
] = NULL
;
201 bool NativeThemeWin::IsClassicTheme(ThemeName name
) const {
202 return !theme_dll_
|| !GetThemeHandle(name
);
206 NativeThemeWin
* NativeThemeWin::instance() {
207 CR_DEFINE_STATIC_LOCAL(NativeThemeWin
, s_native_theme
, ());
208 return &s_native_theme
;
211 gfx::Size
NativeThemeWin::GetPartSize(Part part
,
213 const ExtraParams
& extra
) const {
214 gfx::Size part_size
= CommonThemeGetPartSize(part
, state
, extra
);
215 if (!part_size
.IsEmpty())
218 // The GetThemePartSize call below returns the default size without
219 // accounting for user customization (crbug/218291).
221 case kScrollbarDownArrow
:
222 case kScrollbarLeftArrow
:
223 case kScrollbarRightArrow
:
224 case kScrollbarUpArrow
:
225 case kScrollbarHorizontalThumb
:
226 case kScrollbarVerticalThumb
:
227 case kScrollbarHorizontalTrack
:
228 case kScrollbarVerticalTrack
: {
229 int size
= gfx::win::GetSystemMetricsInDIP(SM_CXVSCROLL
);
232 return gfx::Size(size
, size
);
238 int part_id
= GetWindowsPart(part
, state
, extra
);
239 int state_id
= GetWindowsState(part
, state
, extra
);
241 base::win::ScopedGetDC
screen_dc(NULL
);
243 if (SUCCEEDED(GetThemePartSize(GetThemeName(part
), screen_dc
, part_id
,
244 state_id
, NULL
, TS_TRUE
, &size
)))
245 return gfx::Size(size
.cx
, size
.cy
);
247 // TODO(rogerta): For now, we need to support radio buttons and checkboxes
248 // when theming is not enabled. Support for other parts can be added
250 return (part
== kCheckbox
|| part
== kRadio
) ?
251 gfx::Size(13, 13) : gfx::Size();
254 void NativeThemeWin::Paint(SkCanvas
* canvas
,
257 const gfx::Rect
& rect
,
258 const ExtraParams
& extra
) const {
264 CommonThemePaintComboboxArrow(canvas
, rect
);
266 case kMenuPopupGutter
:
267 CommonThemePaintMenuGutter(canvas
, rect
);
269 case kMenuPopupSeparator
:
270 CommonThemePaintMenuSeparator(canvas
, rect
, extra
.menu_separator
);
272 case kMenuPopupBackground
:
273 CommonThemePaintMenuBackground(canvas
, rect
);
275 case kMenuItemBackground
:
276 CommonThemePaintMenuItemBackground(canvas
, state
, rect
);
282 bool needs_paint_indirect
= false;
283 if (!skia::SupportsPlatformPaint(canvas
)) {
284 // This block will only get hit with --enable-accelerated-drawing flag.
285 needs_paint_indirect
= true;
287 // Scrollbar components on Windows Classic theme (on all Windows versions)
288 // have particularly problematic alpha values, so always draw them
289 // indirectly. In addition, scrollbar thumbs and grippers for the Windows XP
290 // theme (available only on Windows XP) also need their alpha values
293 case kScrollbarDownArrow
:
294 case kScrollbarUpArrow
:
295 case kScrollbarLeftArrow
:
296 case kScrollbarRightArrow
:
297 needs_paint_indirect
= !GetThemeHandle(SCROLLBAR
);
299 case kScrollbarHorizontalThumb
:
300 case kScrollbarVerticalThumb
:
301 case kScrollbarHorizontalGripper
:
302 case kScrollbarVerticalGripper
:
303 needs_paint_indirect
= !GetThemeHandle(SCROLLBAR
) ||
304 base::win::GetVersion() == base::win::VERSION_XP
;
311 if (needs_paint_indirect
)
312 PaintIndirect(canvas
, part
, state
, rect
, extra
);
314 PaintDirect(canvas
, part
, state
, rect
, extra
);
317 NativeThemeWin::NativeThemeWin()
318 : theme_dll_(LoadLibrary(L
"uxtheme.dll")),
320 draw_theme_ex_(NULL
),
321 get_theme_color_(NULL
),
322 get_theme_content_rect_(NULL
),
323 get_theme_part_size_(NULL
),
326 set_theme_properties_(NULL
),
327 is_theme_active_(NULL
),
328 get_theme_int_(NULL
),
329 color_change_listener_(this),
330 is_using_high_contrast_(false),
331 is_using_high_contrast_valid_(false) {
333 draw_theme_
= reinterpret_cast<DrawThemeBackgroundPtr
>(
334 GetProcAddress(theme_dll_
, "DrawThemeBackground"));
335 draw_theme_ex_
= reinterpret_cast<DrawThemeBackgroundExPtr
>(
336 GetProcAddress(theme_dll_
, "DrawThemeBackgroundEx"));
337 get_theme_color_
= reinterpret_cast<GetThemeColorPtr
>(
338 GetProcAddress(theme_dll_
, "GetThemeColor"));
339 get_theme_content_rect_
= reinterpret_cast<GetThemeContentRectPtr
>(
340 GetProcAddress(theme_dll_
, "GetThemeBackgroundContentRect"));
341 get_theme_part_size_
= reinterpret_cast<GetThemePartSizePtr
>(
342 GetProcAddress(theme_dll_
, "GetThemePartSize"));
343 open_theme_
= reinterpret_cast<OpenThemeDataPtr
>(
344 GetProcAddress(theme_dll_
, "OpenThemeData"));
345 close_theme_
= reinterpret_cast<CloseThemeDataPtr
>(
346 GetProcAddress(theme_dll_
, "CloseThemeData"));
347 set_theme_properties_
= reinterpret_cast<SetThemeAppPropertiesPtr
>(
348 GetProcAddress(theme_dll_
, "SetThemeAppProperties"));
349 is_theme_active_
= reinterpret_cast<IsThemeActivePtr
>(
350 GetProcAddress(theme_dll_
, "IsThemeActive"));
351 get_theme_int_
= reinterpret_cast<GetThemeIntPtr
>(
352 GetProcAddress(theme_dll_
, "GetThemeInt"));
354 memset(theme_handles_
, 0, sizeof(theme_handles_
));
356 // Initialize the cached system colors.
357 UpdateSystemColors();
360 NativeThemeWin::~NativeThemeWin() {
362 // todo (cpu): fix this soon. Making a call to CloseHandles() here breaks
363 // certain tests and the reliability bots.
365 FreeLibrary(theme_dll_
);
369 void NativeThemeWin::OnSysColorChange() {
370 UpdateSystemColors();
371 is_using_high_contrast_valid_
= false;
375 void NativeThemeWin::UpdateSystemColors() {
376 for (int i
= 0; i
< arraysize(kSystemColors
); ++i
) {
377 system_colors_
[kSystemColors
[i
]] =
378 color_utils::GetSysSkColor(kSystemColors
[i
]);
382 void NativeThemeWin::PaintDirect(SkCanvas
* canvas
,
385 const gfx::Rect
& rect
,
386 const ExtraParams
& extra
) const {
387 skia::ScopedPlatformPaint
scoped_platform_paint(canvas
);
388 HDC hdc
= scoped_platform_paint
.GetPlatformSurface();
392 PaintCheckbox(hdc
, part
, state
, rect
, extra
.button
);
394 case kInnerSpinButton
:
395 PaintSpinButton(hdc
, part
, state
, rect
, extra
.inner_spin
);
398 PaintMenuList(hdc
, state
, rect
, extra
.menu_list
);
401 PaintMenuCheck(hdc
, state
, rect
, extra
.menu_check
);
403 case kMenuCheckBackground
:
404 PaintMenuCheckBackground(hdc
, state
, rect
);
406 case kMenuPopupArrow
:
407 PaintMenuArrow(hdc
, state
, rect
, extra
.menu_arrow
);
409 case kMenuPopupBackground
:
410 PaintMenuBackground(hdc
, rect
);
412 case kMenuPopupGutter
:
413 PaintMenuGutter(hdc
, rect
);
415 case kMenuPopupSeparator
:
416 PaintMenuSeparator(hdc
, rect
, extra
.menu_separator
);
418 case kMenuItemBackground
:
419 PaintMenuItemBackground(hdc
, state
, rect
, extra
.menu_item
);
422 PaintProgressBar(hdc
, rect
, extra
.progress_bar
);
425 PaintPushButton(hdc
, part
, state
, rect
, extra
.button
);
428 PaintRadioButton(hdc
, part
, state
, rect
, extra
.button
);
430 case kScrollbarDownArrow
:
431 case kScrollbarUpArrow
:
432 case kScrollbarLeftArrow
:
433 case kScrollbarRightArrow
:
434 PaintScrollbarArrow(hdc
, part
, state
, rect
, extra
.scrollbar_arrow
);
436 case kScrollbarHorizontalThumb
:
437 case kScrollbarVerticalThumb
:
438 case kScrollbarHorizontalGripper
:
439 case kScrollbarVerticalGripper
:
440 PaintScrollbarThumb(hdc
, part
, state
, rect
, extra
.scrollbar_thumb
);
442 case kScrollbarHorizontalTrack
:
443 case kScrollbarVerticalTrack
:
444 PaintScrollbarTrack(canvas
, hdc
, part
, state
, rect
,
445 extra
.scrollbar_track
);
447 case kScrollbarCorner
:
448 canvas
->drawColor(SK_ColorWHITE
, SkXfermode::kSrc_Mode
);
450 case kTabPanelBackground
:
451 PaintTabPanelBackground(hdc
, rect
);
454 PaintTextField(hdc
, part
, state
, rect
, extra
.text_field
);
458 PaintTrackbar(canvas
, hdc
, part
, state
, rect
, extra
.trackbar
);
460 case kWindowResizeGripper
:
461 PaintWindowResizeGripper(hdc
, rect
);
471 SkColor
NativeThemeWin::GetSystemColor(ColorId color_id
) const {
473 if (CommonThemeGetSystemColor(color_id
, &color
))
478 case kColorId_WindowBackground
:
479 return system_colors_
[COLOR_WINDOW
];
482 case kColorId_DialogBackground
:
483 return gfx::IsInvertedColorScheme() ?
484 color_utils::InvertColor(kDialogBackgroundColor
) :
485 kDialogBackgroundColor
;
488 case kColorId_FocusedBorderColor
:
489 return kFocusedBorderColor
;
490 case kColorId_UnfocusedBorderColor
:
491 return kUnfocusedBorderColor
;
494 case kColorId_ButtonBackgroundColor
:
495 return kButtonBackgroundColor
;
496 case kColorId_ButtonEnabledColor
:
497 return system_colors_
[COLOR_BTNTEXT
];
498 case kColorId_ButtonDisabledColor
:
499 return system_colors_
[COLOR_GRAYTEXT
];
500 case kColorId_ButtonHighlightColor
:
501 return kButtonHighlightColor
;
502 case kColorId_ButtonHoverColor
:
503 return kButtonHoverColor
;
504 case kColorId_ButtonHoverBackgroundColor
:
505 return kButtonHoverBackgroundColor
;
506 case kColorId_BlueButtonEnabledColor
:
507 case kColorId_BlueButtonDisabledColor
:
508 case kColorId_BlueButtonPressedColor
:
509 case kColorId_BlueButtonHoverColor
:
511 return kInvalidColorIdColor
;
514 case kColorId_EnabledMenuItemForegroundColor
:
515 return kEnabledMenuItemForegroundColor
;
516 case kColorId_DisabledMenuItemForegroundColor
:
517 return kDisabledMenuItemForegroundColor
;
518 case kColorId_DisabledEmphasizedMenuItemForegroundColor
:
519 return SK_ColorBLACK
;
520 case kColorId_FocusedMenuItemBackgroundColor
:
521 return kFocusedMenuItemBackgroundColor
;
522 case kColorId_MenuSeparatorColor
:
523 return kMenuSeparatorColor
;
524 case kColorId_SelectedMenuItemForegroundColor
:
525 case kColorId_HoverMenuItemBackgroundColor
:
526 case kColorId_MenuBackgroundColor
:
527 case kColorId_MenuBorderColor
:
529 return kInvalidColorIdColor
;
532 case kColorId_EnabledMenuButtonBorderColor
:
533 case kColorId_FocusedMenuButtonBorderColor
:
534 case kColorId_HoverMenuButtonBorderColor
:
536 return kInvalidColorIdColor
;
539 case kColorId_LabelEnabledColor
:
540 return system_colors_
[COLOR_BTNTEXT
];
541 case kColorId_LabelDisabledColor
:
542 return system_colors_
[COLOR_GRAYTEXT
];
543 case kColorId_LabelBackgroundColor
:
544 return system_colors_
[COLOR_WINDOW
];
547 case kColorId_TextfieldDefaultColor
:
548 return system_colors_
[COLOR_WINDOWTEXT
];
549 case kColorId_TextfieldDefaultBackground
:
550 return system_colors_
[COLOR_WINDOW
];
551 case kColorId_TextfieldReadOnlyColor
:
552 return system_colors_
[COLOR_GRAYTEXT
];
553 case kColorId_TextfieldReadOnlyBackground
:
554 return system_colors_
[COLOR_3DFACE
];
555 case kColorId_TextfieldSelectionColor
:
556 return system_colors_
[COLOR_HIGHLIGHTTEXT
];
557 case kColorId_TextfieldSelectionBackgroundFocused
:
558 return system_colors_
[COLOR_HIGHLIGHT
];
561 case kColorId_TooltipBackground
:
562 case kColorId_TooltipText
:
564 return kInvalidColorIdColor
;
567 // NOTE: these aren't right for all themes, but as close as I could get.
568 case kColorId_TreeBackground
:
569 return system_colors_
[COLOR_WINDOW
];
570 case kColorId_TreeText
:
571 return system_colors_
[COLOR_WINDOWTEXT
];
572 case kColorId_TreeSelectedText
:
573 return system_colors_
[COLOR_HIGHLIGHTTEXT
];
574 case kColorId_TreeSelectedTextUnfocused
:
575 return system_colors_
[COLOR_BTNTEXT
];
576 case kColorId_TreeSelectionBackgroundFocused
:
577 return system_colors_
[COLOR_HIGHLIGHT
];
578 case kColorId_TreeSelectionBackgroundUnfocused
:
579 return system_colors_
[IsUsingHighContrastTheme() ?
580 COLOR_MENUHIGHLIGHT
: COLOR_BTNFACE
];
581 case kColorId_TreeArrow
:
582 return system_colors_
[COLOR_WINDOWTEXT
];
585 case kColorId_TableBackground
:
586 return system_colors_
[COLOR_WINDOW
];
587 case kColorId_TableText
:
588 return system_colors_
[COLOR_WINDOWTEXT
];
589 case kColorId_TableSelectedText
:
590 return system_colors_
[COLOR_HIGHLIGHTTEXT
];
591 case kColorId_TableSelectedTextUnfocused
:
592 return system_colors_
[COLOR_BTNTEXT
];
593 case kColorId_TableSelectionBackgroundFocused
:
594 return system_colors_
[COLOR_HIGHLIGHT
];
595 case kColorId_TableSelectionBackgroundUnfocused
:
596 return system_colors_
[IsUsingHighContrastTheme() ?
597 COLOR_MENUHIGHLIGHT
: COLOR_BTNFACE
];
598 case kColorId_TableGroupingIndicatorColor
:
599 return system_colors_
[COLOR_GRAYTEXT
];
602 case kColorId_ResultsTableNormalBackground
:
603 return system_colors_
[COLOR_WINDOW
];
604 case kColorId_ResultsTableHoveredBackground
:
605 return color_utils::AlphaBlend(system_colors_
[COLOR_HIGHLIGHT
],
606 system_colors_
[COLOR_WINDOW
], 0x40);
607 case kColorId_ResultsTableSelectedBackground
:
608 return system_colors_
[COLOR_HIGHLIGHT
];
609 case kColorId_ResultsTableNormalText
:
610 case kColorId_ResultsTableHoveredText
:
611 return system_colors_
[COLOR_WINDOWTEXT
];
612 case kColorId_ResultsTableSelectedText
:
613 return system_colors_
[COLOR_HIGHLIGHTTEXT
];
614 case kColorId_ResultsTableNormalDimmedText
:
615 return color_utils::AlphaBlend(system_colors_
[COLOR_WINDOWTEXT
],
616 system_colors_
[COLOR_WINDOW
], 0x80);
617 case kColorId_ResultsTableHoveredDimmedText
:
618 return color_utils::AlphaBlend(
619 system_colors_
[COLOR_WINDOWTEXT
],
620 GetSystemColor(kColorId_ResultsTableHoveredBackground
), 0x80);
621 case kColorId_ResultsTableSelectedDimmedText
:
622 return color_utils::AlphaBlend(system_colors_
[COLOR_HIGHLIGHTTEXT
],
623 system_colors_
[COLOR_HIGHLIGHT
], 0x80);
624 case kColorId_ResultsTableNormalUrl
:
625 return color_utils::GetReadableColor(SkColorSetRGB(0, 128, 0),
626 system_colors_
[COLOR_WINDOW
]);
627 case kColorId_ResultsTableHoveredUrl
:
628 return color_utils::GetReadableColor(
629 SkColorSetRGB(0, 128, 0),
630 GetSystemColor(kColorId_ResultsTableHoveredBackground
));
631 case kColorId_ResultsTableSelectedUrl
:
632 return color_utils::GetReadableColor(SkColorSetRGB(0, 128, 0),
633 system_colors_
[COLOR_HIGHLIGHT
]);
634 case kColorId_ResultsTableNormalDivider
:
635 return color_utils::AlphaBlend(system_colors_
[COLOR_WINDOWTEXT
],
636 system_colors_
[COLOR_WINDOW
], 0x34);
637 case kColorId_ResultsTableHoveredDivider
:
638 return color_utils::AlphaBlend(
639 system_colors_
[COLOR_WINDOWTEXT
],
640 GetSystemColor(kColorId_ResultsTableHoveredBackground
), 0x34);
641 case kColorId_ResultsTableSelectedDivider
:
642 return color_utils::AlphaBlend(system_colors_
[COLOR_HIGHLIGHTTEXT
],
643 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
));
664 SkCanvas
offscreen_canvas(device
.get());
665 DCHECK(skia::SupportsPlatformPaint(&offscreen_canvas
));
667 // Some of the Windows theme drawing operations do not write correct alpha
668 // values for fully-opaque pixels; instead the pixels get alpha 0. This is
669 // especially a problem on Windows XP or when using the Classic theme.
671 // To work-around this, mark all pixels with a placeholder value, to detect
672 // which pixels get touched by the paint operation. After paint, set any
673 // pixels that have alpha 0 to opaque and placeholders to fully-transparent.
674 const SkColor placeholder
= SkColorSetARGB(1, 0, 0, 0);
675 offscreen_canvas
.clear(placeholder
);
677 // Offset destination rects to have origin (0,0).
678 gfx::Rect
adjusted_rect(rect
.size());
679 ExtraParams
adjusted_extra(extra
);
682 adjusted_extra
.progress_bar
.value_rect_x
= 0;
683 adjusted_extra
.progress_bar
.value_rect_y
= 0;
685 case kScrollbarHorizontalTrack
:
686 case kScrollbarVerticalTrack
:
687 adjusted_extra
.scrollbar_track
.track_x
= 0;
688 adjusted_extra
.scrollbar_track
.track_y
= 0;
693 // Draw the theme controls using existing HDC-drawing code.
694 PaintDirect(&offscreen_canvas
, part
, state
, adjusted_rect
, adjusted_extra
);
696 // Copy the pixels to a bitmap that has ref-counted pixel storage, which is
697 // necessary to have when drawing to a SkPicture.
698 const SkBitmap
& hdc_bitmap
=
699 offscreen_canvas
.getDevice()->accessBitmap(false);
701 hdc_bitmap
.copyTo(&bitmap
, kN32_SkColorType
);
703 // Post-process the pixels to fix up the alpha values (see big comment above).
704 const SkPMColor placeholder_value
= SkPreMultiplyColor(placeholder
);
705 const int pixel_count
= rect
.width() * rect
.height();
706 SkPMColor
* pixels
= bitmap
.getAddr32(0, 0);
707 for (int i
= 0; i
< pixel_count
; i
++) {
708 if (pixels
[i
] == placeholder_value
) {
709 // Pixel wasn't touched - make it fully transparent.
710 pixels
[i
] = SkPackARGB32(0, 0, 0, 0);
711 } else if (SkGetPackedA32(pixels
[i
]) == 0) {
712 // Pixel was touched but has incorrect alpha of 0, make it fully opaque.
713 pixels
[i
] = SkPackARGB32(0xFF,
714 SkGetPackedR32(pixels
[i
]),
715 SkGetPackedG32(pixels
[i
]),
716 SkGetPackedB32(pixels
[i
]));
720 // Draw the offscreen bitmap to the destination canvas.
721 canvas
->drawBitmap(bitmap
, rect
.x(), rect
.y());
724 HRESULT
NativeThemeWin::GetThemePartSize(ThemeName theme_name
,
731 HANDLE handle
= GetThemeHandle(theme_name
);
732 return (handle
&& get_theme_part_size_
) ?
733 get_theme_part_size_(handle
, hdc
, part_id
, state_id
, rect
, ts
, size
) :
737 HRESULT
NativeThemeWin::PaintButton(HDC hdc
,
739 const ButtonExtraParams
& extra
,
743 HANDLE handle
= GetThemeHandle(BUTTON
);
744 if (handle
&& draw_theme_
)
745 return draw_theme_(handle
, hdc
, part_id
, state_id
, rect
, NULL
);
747 // Adjust classic_state based on part, state, and extras.
748 int classic_state
= extra
.classic_state
;
751 classic_state
|= DFCS_BUTTONCHECK
;
754 classic_state
|= DFCS_BUTTONRADIO
;
757 classic_state
|= DFCS_BUTTONPUSH
;
766 classic_state
|= DFCS_INACTIVE
;
772 classic_state
|= DFCS_PUSHED
;
780 classic_state
|= DFCS_CHECKED
;
783 // All pressed states have both low bits set, and no other states do.
784 const bool focused
= ((state_id
& ETS_FOCUSED
) == ETS_FOCUSED
);
785 const bool pressed
= ((state_id
& PBS_PRESSED
) == PBS_PRESSED
);
786 if ((BP_PUSHBUTTON
== part_id
) && (pressed
|| focused
)) {
787 // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the
788 // button itself is shrunk by 1 pixel.
789 HBRUSH brush
= GetSysColorBrush(COLOR_3DDKSHADOW
);
791 FrameRect(hdc
, rect
, brush
);
792 InflateRect(rect
, -1, -1);
795 DrawFrameControl(hdc
, rect
, DFC_BUTTON
, classic_state
);
797 // Draw the focus rectangle (the dotted line box) only on buttons. For radio
798 // and checkboxes, we let webkit draw the focus rectangle (orange glow).
799 if ((BP_PUSHBUTTON
== part_id
) && focused
) {
800 // The focus rect is inside the button. The exact number of pixels depends
801 // on whether we're in classic mode or using uxtheme.
802 if (handle
&& get_theme_content_rect_
) {
803 get_theme_content_rect_(handle
, hdc
, part_id
, state_id
, rect
, rect
);
805 InflateRect(rect
, -GetSystemMetrics(SM_CXEDGE
),
806 -GetSystemMetrics(SM_CYEDGE
));
808 DrawFocusRect(hdc
, rect
);
811 // Classic theme doesn't support indeterminate checkboxes. We draw
812 // a recangle inside a checkbox like IE10 does.
813 if (part_id
== BP_CHECKBOX
&& extra
.indeterminate
) {
814 RECT inner_rect
= *rect
;
815 // "4 / 13" is same as IE10 in classic theme.
816 int padding
= (inner_rect
.right
- inner_rect
.left
) * 4 / 13;
817 InflateRect(&inner_rect
, -padding
, -padding
);
818 int color_index
= state
== kDisabled
? COLOR_GRAYTEXT
: COLOR_WINDOWTEXT
;
819 FillRect(hdc
, &inner_rect
, GetSysColorBrush(color_index
));
825 HRESULT
NativeThemeWin::PaintMenuSeparator(
827 const gfx::Rect
& rect
,
828 const MenuSeparatorExtraParams
& extra
) const {
829 RECT rect_win
= rect
.ToRECT();
831 HANDLE handle
= GetThemeHandle(MENU
);
832 if (handle
&& draw_theme_
) {
833 // Delta is needed for non-classic to move separator up slightly.
836 return draw_theme_(handle
, hdc
, MENU_POPUPSEPARATOR
, MPI_NORMAL
, &rect_win
,
840 DrawEdge(hdc
, &rect_win
, EDGE_ETCHED
, BF_TOP
);
844 HRESULT
NativeThemeWin::PaintMenuGutter(HDC hdc
,
845 const gfx::Rect
& rect
) const {
846 RECT rect_win
= rect
.ToRECT();
847 HANDLE handle
= GetThemeHandle(MENU
);
848 return (handle
&& draw_theme_
) ?
849 draw_theme_(handle
, hdc
, MENU_POPUPGUTTER
, MPI_NORMAL
, &rect_win
, NULL
) :
853 HRESULT
NativeThemeWin::PaintMenuArrow(
856 const gfx::Rect
& rect
,
857 const MenuArrowExtraParams
& extra
) const {
858 int state_id
= MSM_NORMAL
;
859 if (state
== kDisabled
)
860 state_id
= MSM_DISABLED
;
862 HANDLE handle
= GetThemeHandle(MENU
);
863 RECT rect_win
= rect
.ToRECT();
864 if (handle
&& draw_theme_
) {
865 if (extra
.pointing_right
) {
866 return draw_theme_(handle
, hdc
, MENU_POPUPSUBMENU
, state_id
, &rect_win
,
869 // There is no way to tell the uxtheme API to draw a left pointing arrow; it
870 // doesn't have a flag equivalent to DFCS_MENUARROWRIGHT. But they are
871 // needed for RTL locales on Vista. So use a memory DC and mirror the
872 // region with GDI's StretchBlt.
874 base::win::ScopedCreateDC
mem_dc(CreateCompatibleDC(hdc
));
875 base::win::ScopedBitmap
mem_bitmap(CreateCompatibleBitmap(hdc
, r
.width(),
877 base::win::ScopedSelectObject
select_bitmap(mem_dc
, mem_bitmap
);
878 // Copy and horizontally mirror the background from hdc into mem_dc. Use
879 // a negative-width source rect, starting at the rightmost pixel.
880 StretchBlt(mem_dc
, 0, 0, r
.width(), r
.height(),
881 hdc
, r
.right()-1, r
.y(), -r
.width(), r
.height(), SRCCOPY
);
883 RECT theme_rect
= {0, 0, r
.width(), r
.height()};
884 HRESULT result
= draw_theme_(handle
, mem_dc
, MENU_POPUPSUBMENU
,
885 state_id
, &theme_rect
, NULL
);
886 // Copy and mirror the result back into mem_dc.
887 StretchBlt(hdc
, r
.x(), r
.y(), r
.width(), r
.height(),
888 mem_dc
, r
.width()-1, 0, -r
.width(), r
.height(), SRCCOPY
);
892 // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
893 // left pointing arrow. This makes the following statement counterintuitive.
894 UINT pfc_state
= extra
.pointing_right
? DFCS_MENUARROW
: DFCS_MENUARROWRIGHT
;
895 return PaintFrameControl(hdc
, rect
, DFC_MENU
, pfc_state
, extra
.is_selected
,
899 HRESULT
NativeThemeWin::PaintMenuBackground(HDC hdc
,
900 const gfx::Rect
& rect
) const {
901 HANDLE handle
= GetThemeHandle(MENU
);
902 RECT rect_win
= rect
.ToRECT();
903 if (handle
&& draw_theme_
) {
904 HRESULT result
= draw_theme_(handle
, hdc
, MENU_POPUPBACKGROUND
, 0,
906 FrameRect(hdc
, &rect_win
, GetSysColorBrush(COLOR_3DSHADOW
));
910 FillRect(hdc
, &rect_win
, GetSysColorBrush(COLOR_MENU
));
911 DrawEdge(hdc
, &rect_win
, EDGE_RAISED
, BF_RECT
);
915 HRESULT
NativeThemeWin::PaintMenuCheck(
918 const gfx::Rect
& rect
,
919 const MenuCheckExtraParams
& extra
) const {
920 HANDLE handle
= GetThemeHandle(MENU
);
921 if (handle
&& draw_theme_
) {
922 const int state_id
= extra
.is_radio
?
923 ((state
== kDisabled
) ? MC_BULLETDISABLED
: MC_BULLETNORMAL
) :
924 ((state
== kDisabled
) ? MC_CHECKMARKDISABLED
: MC_CHECKMARKNORMAL
);
925 RECT rect_win
= rect
.ToRECT();
926 return draw_theme_(handle
, hdc
, MENU_POPUPCHECK
, state_id
, &rect_win
, NULL
);
929 return PaintFrameControl(hdc
, rect
, DFC_MENU
,
930 extra
.is_radio
? DFCS_MENUBULLET
: DFCS_MENUCHECK
,
931 extra
.is_selected
, state
);
934 HRESULT
NativeThemeWin::PaintMenuCheckBackground(HDC hdc
,
936 const gfx::Rect
& rect
) const {
937 HANDLE handle
= GetThemeHandle(MENU
);
938 if (!handle
|| !draw_theme_
)
939 return S_OK
; // Nothing to do for background.
941 int state_id
= state
== kDisabled
? MCB_DISABLED
: MCB_NORMAL
;
942 RECT rect_win
= rect
.ToRECT();
943 return draw_theme_(handle
, hdc
, MENU_POPUPCHECKBACKGROUND
, state_id
,
947 HRESULT
NativeThemeWin::PaintMenuItemBackground(
950 const gfx::Rect
& rect
,
951 const MenuItemExtraParams
& extra
) const {
952 HANDLE handle
= GetThemeHandle(MENU
);
953 RECT rect_win
= rect
.ToRECT();
954 int state_id
= MPI_NORMAL
;
957 state_id
= extra
.is_selected
? MPI_DISABLEDHOT
: MPI_DISABLED
;
970 if (handle
&& draw_theme_
)
971 return draw_theme_(handle
, hdc
, MENU_POPUPITEM
, state_id
, &rect_win
, NULL
);
973 if (extra
.is_selected
)
974 FillRect(hdc
, &rect_win
, GetSysColorBrush(COLOR_HIGHLIGHT
));
978 HRESULT
NativeThemeWin::PaintPushButton(HDC hdc
,
981 const gfx::Rect
& rect
,
982 const ButtonExtraParams
& extra
) const {
983 int state_id
= extra
.is_default
? PBS_DEFAULTED
: PBS_NORMAL
;
986 state_id
= PBS_DISABLED
;
994 state_id
= PBS_PRESSED
;
1001 RECT rect_win
= rect
.ToRECT();
1002 return PaintButton(hdc
, state
, extra
, BP_PUSHBUTTON
, state_id
, &rect_win
);
1005 HRESULT
NativeThemeWin::PaintRadioButton(HDC hdc
,
1008 const gfx::Rect
& rect
,
1009 const ButtonExtraParams
& extra
) const {
1010 int state_id
= extra
.checked
? RBS_CHECKEDNORMAL
: RBS_UNCHECKEDNORMAL
;
1013 state_id
= extra
.checked
? RBS_CHECKEDDISABLED
: RBS_UNCHECKEDDISABLED
;
1016 state_id
= extra
.checked
? RBS_CHECKEDHOT
: RBS_UNCHECKEDHOT
;
1021 state_id
= extra
.checked
? RBS_CHECKEDPRESSED
: RBS_UNCHECKEDPRESSED
;
1028 RECT rect_win
= rect
.ToRECT();
1029 return PaintButton(hdc
, state
, extra
, BP_RADIOBUTTON
, state_id
, &rect_win
);
1032 HRESULT
NativeThemeWin::PaintCheckbox(HDC hdc
,
1035 const gfx::Rect
& rect
,
1036 const ButtonExtraParams
& extra
) const {
1037 int state_id
= extra
.checked
?
1039 (extra
.indeterminate
? CBS_MIXEDNORMAL
: CBS_UNCHECKEDNORMAL
);
1042 state_id
= extra
.checked
?
1043 CBS_CHECKEDDISABLED
:
1044 (extra
.indeterminate
? CBS_MIXEDDISABLED
: CBS_UNCHECKEDDISABLED
);
1047 state_id
= extra
.checked
?
1049 (extra
.indeterminate
? CBS_MIXEDHOT
: CBS_UNCHECKEDHOT
);
1054 state_id
= extra
.checked
?
1055 CBS_CHECKEDPRESSED
:
1056 (extra
.indeterminate
? CBS_MIXEDPRESSED
: CBS_UNCHECKEDPRESSED
);
1063 RECT rect_win
= rect
.ToRECT();
1064 return PaintButton(hdc
, state
, extra
, BP_CHECKBOX
, state_id
, &rect_win
);
1067 HRESULT
NativeThemeWin::PaintMenuList(HDC hdc
,
1069 const gfx::Rect
& rect
,
1070 const MenuListExtraParams
& extra
) const {
1071 HANDLE handle
= GetThemeHandle(MENULIST
);
1072 RECT rect_win
= rect
.ToRECT();
1073 int state_id
= CBXS_NORMAL
;
1076 state_id
= CBXS_DISABLED
;
1079 state_id
= CBXS_HOT
;
1084 state_id
= CBXS_PRESSED
;
1091 if (handle
&& draw_theme_
)
1092 return draw_theme_(handle
, hdc
, CP_DROPDOWNBUTTON
, state_id
, &rect_win
,
1095 // Draw it manually.
1096 DrawFrameControl(hdc
, &rect_win
, DFC_SCROLL
,
1097 DFCS_SCROLLCOMBOBOX
| extra
.classic_state
);
1101 HRESULT
NativeThemeWin::PaintScrollbarArrow(
1105 const gfx::Rect
& rect
,
1106 const ScrollbarArrowExtraParams
& extra
) const {
1107 static const int state_id_matrix
[4][kNumStates
] = {
1108 ABS_DOWNDISABLED
, ABS_DOWNHOT
, ABS_DOWNNORMAL
, ABS_DOWNPRESSED
,
1109 ABS_LEFTDISABLED
, ABS_LEFTHOT
, ABS_LEFTNORMAL
, ABS_LEFTPRESSED
,
1110 ABS_RIGHTDISABLED
, ABS_RIGHTHOT
, ABS_RIGHTNORMAL
, ABS_RIGHTPRESSED
,
1111 ABS_UPDISABLED
, ABS_UPHOT
, ABS_UPNORMAL
, ABS_UPPRESSED
1113 HANDLE handle
= GetThemeHandle(SCROLLBAR
);
1114 RECT rect_win
= rect
.ToRECT();
1115 if (handle
&& draw_theme_
) {
1116 int index
= part
- kScrollbarDownArrow
;
1117 DCHECK_GE(index
, 0);
1118 DCHECK_LT(static_cast<size_t>(index
), arraysize(state_id_matrix
));
1119 int state_id
= state_id_matrix
[index
][state
];
1121 // Hovering means that the cursor is over the scroolbar, but not over the
1122 // specific arrow itself. We don't want to show it "hot" mode, but only
1124 if (state
== kHovered
&& extra
.is_hovering
) {
1126 case kScrollbarDownArrow
:
1127 state_id
= ABS_DOWNHOVER
;
1129 case kScrollbarLeftArrow
:
1130 state_id
= ABS_LEFTHOVER
;
1132 case kScrollbarRightArrow
:
1133 state_id
= ABS_RIGHTHOVER
;
1135 case kScrollbarUpArrow
:
1136 state_id
= ABS_UPHOVER
;
1143 return PaintScaledTheme(handle
, hdc
, SBP_ARROWBTN
, state_id
, rect
);
1146 int classic_state
= DFCS_SCROLLDOWN
;
1148 case kScrollbarDownArrow
:
1150 case kScrollbarLeftArrow
:
1151 classic_state
= DFCS_SCROLLLEFT
;
1153 case kScrollbarRightArrow
:
1154 classic_state
= DFCS_SCROLLRIGHT
;
1156 case kScrollbarUpArrow
:
1157 classic_state
= DFCS_SCROLLUP
;
1165 classic_state
|= DFCS_INACTIVE
;
1168 classic_state
|= DFCS_HOT
;
1173 classic_state
|= DFCS_PUSHED
;
1179 DrawFrameControl(hdc
, &rect_win
, DFC_SCROLL
, classic_state
);
1183 HRESULT
NativeThemeWin::PaintScrollbarThumb(
1187 const gfx::Rect
& rect
,
1188 const ScrollbarThumbExtraParams
& extra
) const {
1189 HANDLE handle
= GetThemeHandle(SCROLLBAR
);
1190 RECT rect_win
= rect
.ToRECT();
1192 int part_id
= SBP_THUMBBTNVERT
;
1194 case kScrollbarHorizontalThumb
:
1195 part_id
= SBP_THUMBBTNHORZ
;
1197 case kScrollbarVerticalThumb
:
1199 case kScrollbarHorizontalGripper
:
1200 part_id
= SBP_GRIPPERHORZ
;
1202 case kScrollbarVerticalGripper
:
1203 part_id
= SBP_GRIPPERVERT
;
1210 int state_id
= SCRBS_NORMAL
;
1213 state_id
= SCRBS_DISABLED
;
1216 state_id
= extra
.is_hovering
? SCRBS_HOVER
: SCRBS_HOT
;
1221 state_id
= SCRBS_PRESSED
;
1228 if (handle
&& draw_theme_
)
1229 return PaintScaledTheme(handle
, hdc
, part_id
, state_id
, rect
);
1231 // Draw it manually.
1232 if ((part_id
== SBP_THUMBBTNHORZ
) || (part_id
== SBP_THUMBBTNVERT
))
1233 DrawEdge(hdc
, &rect_win
, EDGE_RAISED
, BF_RECT
| BF_MIDDLE
);
1234 // Classic mode doesn't have a gripper.
1238 HRESULT
NativeThemeWin::PaintScrollbarTrack(
1243 const gfx::Rect
& rect
,
1244 const ScrollbarTrackExtraParams
& extra
) const {
1245 HANDLE handle
= GetThemeHandle(SCROLLBAR
);
1246 RECT rect_win
= rect
.ToRECT();
1248 const int part_id
= extra
.is_upper
?
1249 ((part
== kScrollbarHorizontalTrack
) ?
1250 SBP_UPPERTRACKHORZ
: SBP_UPPERTRACKVERT
) :
1251 ((part
== kScrollbarHorizontalTrack
) ?
1252 SBP_LOWERTRACKHORZ
: SBP_LOWERTRACKVERT
);
1254 int state_id
= SCRBS_NORMAL
;
1257 state_id
= SCRBS_DISABLED
;
1260 state_id
= SCRBS_HOVER
;
1265 state_id
= SCRBS_PRESSED
;
1272 if (handle
&& draw_theme_
)
1273 return draw_theme_(handle
, hdc
, part_id
, state_id
, &rect_win
, NULL
);
1275 // Draw it manually.
1276 if ((system_colors_
[COLOR_SCROLLBAR
] != system_colors_
[COLOR_3DFACE
]) &&
1277 (system_colors_
[COLOR_SCROLLBAR
] != system_colors_
[COLOR_WINDOW
])) {
1278 FillRect(hdc
, &rect_win
, reinterpret_cast<HBRUSH
>(COLOR_SCROLLBAR
+ 1));
1281 RECT align_rect
= gfx::Rect(extra
.track_x
, extra
.track_y
, extra
.track_width
,
1282 extra
.track_height
).ToRECT();
1283 SetCheckerboardShader(&paint
, align_rect
);
1284 canvas
->drawIRect(skia::RECTToSkIRect(rect_win
), paint
);
1286 if (extra
.classic_state
& DFCS_PUSHED
)
1287 InvertRect(hdc
, &rect_win
);
1291 HRESULT
NativeThemeWin::PaintSpinButton(
1295 const gfx::Rect
& rect
,
1296 const InnerSpinButtonExtraParams
& extra
) const {
1297 HANDLE handle
= GetThemeHandle(SPIN
);
1298 RECT rect_win
= rect
.ToRECT();
1299 int part_id
= extra
.spin_up
? SPNP_UP
: SPNP_DOWN
;
1300 int state_id
= extra
.spin_up
? UPS_NORMAL
: DNS_NORMAL
;
1303 state_id
= extra
.spin_up
? UPS_DISABLED
: DNS_DISABLED
;
1306 state_id
= extra
.spin_up
? UPS_HOT
: DNS_HOT
;
1311 state_id
= extra
.spin_up
? UPS_PRESSED
: DNS_PRESSED
;
1318 if (handle
&& draw_theme_
)
1319 return draw_theme_(handle
, hdc
, part_id
, state_id
, &rect_win
, NULL
);
1320 DrawFrameControl(hdc
, &rect_win
, DFC_SCROLL
, extra
.classic_state
);
1324 HRESULT
NativeThemeWin::PaintTrackbar(
1329 const gfx::Rect
& rect
,
1330 const TrackbarExtraParams
& extra
) const {
1331 const int part_id
= extra
.vertical
?
1332 ((part
== kTrackbarTrack
) ? TKP_TRACKVERT
: TKP_THUMBVERT
) :
1333 ((part
== kTrackbarTrack
) ? TKP_TRACK
: TKP_THUMBBOTTOM
);
1335 int state_id
= TUS_NORMAL
;
1338 state_id
= TUS_DISABLED
;
1346 state_id
= TUS_PRESSED
;
1353 // Make the channel be 4 px thick in the center of the supplied rect. (4 px
1354 // matches what XP does in various menus; GetThemePartSize() doesn't seem to
1355 // return good values here.)
1356 RECT rect_win
= rect
.ToRECT();
1357 RECT channel_rect
= rect
.ToRECT();
1358 const int channel_thickness
= 4;
1359 if (part_id
== TKP_TRACK
) {
1361 ((channel_rect
.bottom
- channel_rect
.top
- channel_thickness
) / 2);
1362 channel_rect
.bottom
= channel_rect
.top
+ channel_thickness
;
1363 } else if (part_id
== TKP_TRACKVERT
) {
1364 channel_rect
.left
+=
1365 ((channel_rect
.right
- channel_rect
.left
- channel_thickness
) / 2);
1366 channel_rect
.right
= channel_rect
.left
+ channel_thickness
;
1367 } // else this isn't actually a channel, so |channel_rect| == |rect|.
1369 HANDLE handle
= GetThemeHandle(TRACKBAR
);
1370 if (handle
&& draw_theme_
)
1371 return draw_theme_(handle
, hdc
, part_id
, state_id
, &channel_rect
, NULL
);
1373 // Classic mode, draw it manually.
1374 if ((part_id
== TKP_TRACK
) || (part_id
== TKP_TRACKVERT
)) {
1375 DrawEdge(hdc
, &channel_rect
, EDGE_SUNKEN
, BF_RECT
);
1376 } else if (part_id
== TKP_THUMBVERT
) {
1377 DrawEdge(hdc
, &rect_win
, EDGE_RAISED
, BF_RECT
| BF_SOFT
| BF_MIDDLE
);
1379 // Split rect into top and bottom pieces.
1380 RECT top_section
= rect
.ToRECT();
1381 RECT bottom_section
= rect
.ToRECT();
1382 top_section
.bottom
-= ((bottom_section
.right
- bottom_section
.left
) / 2);
1383 bottom_section
.top
= top_section
.bottom
;
1384 DrawEdge(hdc
, &top_section
, EDGE_RAISED
,
1385 BF_LEFT
| BF_TOP
| BF_RIGHT
| BF_SOFT
| BF_MIDDLE
| BF_ADJUST
);
1387 // Split triangular piece into two diagonals.
1388 RECT
& left_half
= bottom_section
;
1389 RECT right_half
= bottom_section
;
1390 right_half
.left
+= ((bottom_section
.right
- bottom_section
.left
) / 2);
1391 left_half
.right
= right_half
.left
;
1392 DrawEdge(hdc
, &left_half
, EDGE_RAISED
,
1393 BF_DIAGONAL_ENDTOPLEFT
| BF_SOFT
| BF_MIDDLE
| BF_ADJUST
);
1394 DrawEdge(hdc
, &right_half
, EDGE_RAISED
,
1395 BF_DIAGONAL_ENDBOTTOMLEFT
| BF_SOFT
| BF_MIDDLE
| BF_ADJUST
);
1397 // If the button is pressed, draw hatching.
1398 if (extra
.classic_state
& DFCS_PUSHED
) {
1400 SetCheckerboardShader(&paint
, rect_win
);
1402 // Fill all three pieces with the pattern.
1403 canvas
->drawIRect(skia::RECTToSkIRect(top_section
), paint
);
1405 SkScalar left_triangle_top
= SkIntToScalar(left_half
.top
);
1406 SkScalar left_triangle_right
= SkIntToScalar(left_half
.right
);
1407 SkPath left_triangle
;
1408 left_triangle
.moveTo(SkIntToScalar(left_half
.left
), left_triangle_top
);
1409 left_triangle
.lineTo(left_triangle_right
, left_triangle_top
);
1410 left_triangle
.lineTo(left_triangle_right
,
1411 SkIntToScalar(left_half
.bottom
));
1412 left_triangle
.close();
1413 canvas
->drawPath(left_triangle
, paint
);
1415 SkScalar right_triangle_left
= SkIntToScalar(right_half
.left
);
1416 SkScalar right_triangle_top
= SkIntToScalar(right_half
.top
);
1417 SkPath right_triangle
;
1418 right_triangle
.moveTo(right_triangle_left
, right_triangle_top
);
1419 right_triangle
.lineTo(SkIntToScalar(right_half
.right
),
1420 right_triangle_top
);
1421 right_triangle
.lineTo(right_triangle_left
,
1422 SkIntToScalar(right_half
.bottom
));
1423 right_triangle
.close();
1424 canvas
->drawPath(right_triangle
, paint
);
1430 HRESULT
NativeThemeWin::PaintProgressBar(
1432 const gfx::Rect
& rect
,
1433 const ProgressBarExtraParams
& extra
) const {
1434 // There is no documentation about the animation speed, frame-rate, nor
1435 // size of moving overlay of the indeterminate progress bar.
1436 // So we just observed real-world programs and guessed following parameters.
1437 const int kDeterminateOverlayPixelsPerSecond
= 300;
1438 const int kDeterminateOverlayWidth
= 120;
1439 const int kIndeterminateOverlayPixelsPerSecond
= 175;
1440 const int kVistaIndeterminateOverlayWidth
= 120;
1441 const int kXPIndeterminateOverlayWidth
= 55;
1442 // The thickness of the bar frame inside |value_rect|
1443 const int kXPBarPadding
= 3;
1445 RECT bar_rect
= rect
.ToRECT();
1446 RECT value_rect
= gfx::Rect(extra
.value_rect_x
,
1448 extra
.value_rect_width
,
1449 extra
.value_rect_height
).ToRECT();
1451 HANDLE handle
= GetThemeHandle(PROGRESS
);
1452 if (!handle
|| !draw_theme_
|| !draw_theme_ex_
) {
1453 FillRect(hdc
, &bar_rect
, GetSysColorBrush(COLOR_BTNFACE
));
1454 FillRect(hdc
, &value_rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
1455 DrawEdge(hdc
, &bar_rect
, EDGE_SUNKEN
, BF_RECT
| BF_ADJUST
);
1459 draw_theme_(handle
, hdc
, PP_BAR
, 0, &bar_rect
, NULL
);
1461 bool pre_vista
= base::win::GetVersion() < base::win::VERSION_VISTA
;
1462 int bar_width
= bar_rect
.right
- bar_rect
.left
;
1463 if (!extra
.determinate
) {
1464 // The glossy overlay for the indeterminate progress bar has a small pause
1465 // after each animation. We emulate this by adding an invisible margin the
1466 // animation has to traverse.
1467 int width_with_margin
= bar_width
+ kIndeterminateOverlayPixelsPerSecond
;
1468 int overlay_width
= pre_vista
?
1469 kXPIndeterminateOverlayWidth
: kVistaIndeterminateOverlayWidth
;
1470 RECT overlay_rect
= bar_rect
;
1471 overlay_rect
.left
+= ComputeAnimationProgress(
1472 width_with_margin
, overlay_width
, kIndeterminateOverlayPixelsPerSecond
,
1473 extra
.animated_seconds
);
1474 overlay_rect
.right
= overlay_rect
.left
+ overlay_width
;
1476 RECT shrunk_rect
= InsetRect(&overlay_rect
, kXPBarPadding
);
1477 RECT shrunk_bar_rect
= InsetRect(&bar_rect
, kXPBarPadding
);
1478 draw_theme_(handle
, hdc
, PP_CHUNK
, 0, &shrunk_rect
, &shrunk_bar_rect
);
1480 draw_theme_(handle
, hdc
, PP_MOVEOVERLAY
, 0, &overlay_rect
, &bar_rect
);
1485 // We care about the direction here because PP_CHUNK painting is asymmetric.
1486 // TODO(morrita): This RTL guess can be wrong. We should pass in the
1487 // direction from WebKit.
1488 const DTBGOPTS value_draw_options
= {
1490 (bar_rect
.right
== value_rect
.right
&& bar_rect
.left
!= value_rect
.left
) ?
1495 // On XP, the progress bar is chunk-style and has no glossy effect. We need
1496 // to shrink the destination rect to fit the part inside the bar with an
1497 // appropriate margin.
1498 RECT shrunk_value_rect
= InsetRect(&value_rect
, kXPBarPadding
);
1499 draw_theme_ex_(handle
, hdc
, PP_CHUNK
, 0, &shrunk_value_rect
,
1500 &value_draw_options
);
1502 // On Vista or later, the progress bar part has a single-block value part
1503 // and a glossy effect. The value part has exactly same height as the bar
1504 // part, so we don't need to shrink the rect.
1505 draw_theme_ex_(handle
, hdc
, PP_FILL
, 0, &value_rect
, &value_draw_options
);
1507 RECT overlay_rect
= value_rect
;
1508 overlay_rect
.left
+= ComputeAnimationProgress(
1509 bar_width
, kDeterminateOverlayWidth
, kDeterminateOverlayPixelsPerSecond
,
1510 extra
.animated_seconds
);
1511 overlay_rect
.right
= overlay_rect
.left
+ kDeterminateOverlayWidth
;
1512 draw_theme_(handle
, hdc
, PP_MOVEOVERLAY
, 0, &overlay_rect
, &value_rect
);
1517 HRESULT
NativeThemeWin::PaintWindowResizeGripper(HDC hdc
,
1518 const gfx::Rect
& rect
) const {
1519 HANDLE handle
= GetThemeHandle(STATUS
);
1520 RECT rect_win
= rect
.ToRECT();
1521 if (handle
&& draw_theme_
) {
1522 // Paint the status bar gripper. There doesn't seem to be a standard
1523 // gripper in Windows for the space between scrollbars. This is pretty
1524 // close, but it's supposed to be painted over a status bar.
1525 return draw_theme_(handle
, hdc
, SP_GRIPPER
, 0, &rect_win
, NULL
);
1528 // Draw a windows classic scrollbar gripper.
1529 DrawFrameControl(hdc
, &rect_win
, DFC_SCROLL
, DFCS_SCROLLSIZEGRIP
);
1533 HRESULT
NativeThemeWin::PaintTabPanelBackground(HDC hdc
,
1534 const gfx::Rect
& rect
) const {
1535 HANDLE handle
= GetThemeHandle(TAB
);
1536 RECT rect_win
= rect
.ToRECT();
1537 if (handle
&& draw_theme_
)
1538 return draw_theme_(handle
, hdc
, TABP_BODY
, 0, &rect_win
, NULL
);
1540 // Classic just renders a flat color background.
1541 FillRect(hdc
, &rect_win
, reinterpret_cast<HBRUSH
>(COLOR_3DFACE
+ 1));
1545 HRESULT
NativeThemeWin::PaintTextField(
1549 const gfx::Rect
& rect
,
1550 const TextFieldExtraParams
& extra
) const {
1551 int state_id
= ETS_NORMAL
;
1554 state_id
= ETS_DISABLED
;
1560 if (extra
.is_read_only
)
1561 state_id
= ETS_READONLY
;
1562 else if (extra
.is_focused
)
1563 state_id
= ETS_FOCUSED
;
1566 state_id
= ETS_SELECTED
;
1573 RECT rect_win
= rect
.ToRECT();
1574 return PaintTextField(hdc
, EP_EDITTEXT
, state_id
, extra
.classic_state
,
1576 skia::SkColorToCOLORREF(extra
.background_color
),
1577 extra
.fill_content_area
, extra
.draw_edges
);
1580 HRESULT
NativeThemeWin::PaintTextField(HDC hdc
,
1586 bool fill_content_area
,
1587 bool draw_edges
) const {
1588 // TODO(ojan): http://b/1210017 Figure out how to give the ability to
1589 // exclude individual edges from being drawn.
1591 HANDLE handle
= GetThemeHandle(TEXTFIELD
);
1592 // TODO(mpcomplete): can we detect if the color is specified by the user,
1593 // and if not, just use the system color?
1594 // CreateSolidBrush() accepts a RGB value but alpha must be 0.
1595 base::win::ScopedGDIObject
<HBRUSH
> bg_brush(CreateSolidBrush(color
));
1596 // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible
1597 // draw_theme_ex_ is NULL and draw_theme_ is non-null.
1598 if (!handle
|| (!draw_theme_ex_
&& (!draw_theme_
|| !draw_edges
))) {
1599 // Draw it manually.
1601 DrawEdge(hdc
, rect
, EDGE_SUNKEN
, BF_RECT
| BF_ADJUST
);
1603 if (fill_content_area
) {
1604 FillRect(hdc
, rect
, (classic_state
& DFCS_INACTIVE
) ?
1605 reinterpret_cast<HBRUSH
>(COLOR_BTNFACE
+ 1) : bg_brush
);
1610 static const DTBGOPTS omit_border_options
= {
1615 HRESULT hr
= draw_theme_ex_
?
1616 draw_theme_ex_(handle
, hdc
, part_id
, state_id
, rect
,
1617 draw_edges
? NULL
: &omit_border_options
) :
1618 draw_theme_(handle
, hdc
, part_id
, state_id
, rect
, NULL
);
1620 // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
1621 if (fill_content_area
&& get_theme_content_rect_
) {
1623 hr
= get_theme_content_rect_(handle
, hdc
, part_id
, state_id
, rect
,
1625 FillRect(hdc
, &content_rect
, bg_brush
);
1630 HRESULT
NativeThemeWin::PaintScaledTheme(HANDLE theme
,
1634 const gfx::Rect
& rect
) const {
1635 // Correct the scaling and positioning of sub-components such as scrollbar
1636 // arrows and thumb grippers in the event that the world transform applies
1637 // scaling (e.g. in high-DPI mode).
1638 XFORM save_transform
;
1639 if (GetWorldTransform(hdc
, &save_transform
)) {
1640 float scale
= save_transform
.eM11
;
1641 if (scale
!= 1 && save_transform
.eM12
== 0) {
1642 ModifyWorldTransform(hdc
, NULL
, MWT_IDENTITY
);
1643 gfx::Rect
scaled_rect(gfx::ToEnclosedRect(gfx::ScaleRect(rect
, scale
)));
1644 scaled_rect
.Offset(save_transform
.eDx
, save_transform
.eDy
);
1645 RECT bounds
= scaled_rect
.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
) {
1663 case kInnerSpinButton
:
1667 case kMenuPopupArrow
:
1668 case kMenuPopupGutter
:
1669 case kMenuPopupSeparator
:
1673 case kScrollbarDownArrow
:
1674 case kScrollbarLeftArrow
:
1675 case kScrollbarRightArrow
:
1676 case kScrollbarUpArrow
:
1677 case kScrollbarHorizontalThumb
:
1678 case kScrollbarVerticalThumb
:
1679 case kScrollbarHorizontalTrack
:
1680 case kScrollbarVerticalTrack
:
1687 case kWindowResizeGripper
:
1689 case kComboboxArrow
:
1690 case kMenuCheckBackground
:
1691 case kMenuPopupBackground
:
1692 case kMenuItemBackground
:
1693 case kScrollbarHorizontalGripper
:
1694 case kScrollbarVerticalGripper
:
1695 case kScrollbarCorner
:
1696 case kTabPanelBackground
:
1697 case kTrackbarThumb
:
1698 case kTrackbarTrack
:
1706 int NativeThemeWin::GetWindowsPart(Part part
,
1708 const ExtraParams
& extra
) {
1713 return MENU_POPUPCHECK
;
1714 case kMenuPopupArrow
:
1715 return MENU_POPUPSUBMENU
;
1716 case kMenuPopupGutter
:
1717 return MENU_POPUPGUTTER
;
1718 case kMenuPopupSeparator
:
1719 return MENU_POPUPSEPARATOR
;
1721 return BP_PUSHBUTTON
;
1723 return BP_RADIOBUTTON
;
1724 case kScrollbarDownArrow
:
1725 case kScrollbarLeftArrow
:
1726 case kScrollbarRightArrow
:
1727 case kScrollbarUpArrow
:
1728 return SBP_ARROWBTN
;
1729 case kScrollbarHorizontalThumb
:
1730 return SBP_THUMBBTNHORZ
;
1731 case kScrollbarVerticalThumb
:
1732 return SBP_THUMBBTNVERT
;
1733 case kWindowResizeGripper
:
1735 case kComboboxArrow
:
1736 case kInnerSpinButton
:
1738 case kMenuCheckBackground
:
1739 case kMenuPopupBackground
:
1740 case kMenuItemBackground
:
1742 case kScrollbarHorizontalTrack
:
1743 case kScrollbarVerticalTrack
:
1744 case kScrollbarHorizontalGripper
:
1745 case kScrollbarVerticalGripper
:
1746 case kScrollbarCorner
:
1749 case kTabPanelBackground
:
1751 case kTrackbarThumb
:
1752 case kTrackbarTrack
:
1759 int NativeThemeWin::GetWindowsState(Part part
,
1761 const ExtraParams
& extra
) {
1766 return CBS_UNCHECKEDDISABLED
;
1768 return CBS_UNCHECKEDHOT
;
1770 return CBS_UNCHECKEDNORMAL
;
1772 return CBS_UNCHECKEDPRESSED
;
1780 return extra
.menu_check
.is_radio
?
1781 MC_BULLETDISABLED
: MC_CHECKMARKDISABLED
;
1785 return extra
.menu_check
.is_radio
?
1786 MC_BULLETNORMAL
: MC_CHECKMARKNORMAL
;
1791 case kMenuPopupArrow
:
1792 case kMenuPopupGutter
:
1793 case kMenuPopupSeparator
:
1796 return MBI_DISABLED
;
1810 return PBS_DISABLED
;
1824 return RBS_UNCHECKEDDISABLED
;
1826 return RBS_UNCHECKEDHOT
;
1828 return RBS_UNCHECKEDNORMAL
;
1830 return RBS_UNCHECKEDPRESSED
;
1835 case kScrollbarDownArrow
:
1838 return ABS_DOWNDISABLED
;
1840 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1841 return base::win::GetVersion() < base::win::VERSION_VISTA
?
1842 ABS_DOWNHOT
: ABS_DOWNHOVER
;
1844 return ABS_DOWNNORMAL
;
1846 return ABS_DOWNPRESSED
;
1851 case kScrollbarLeftArrow
:
1854 return ABS_LEFTDISABLED
;
1856 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1857 return base::win::GetVersion() < base::win::VERSION_VISTA
?
1858 ABS_LEFTHOT
: ABS_LEFTHOVER
;
1860 return ABS_LEFTNORMAL
;
1862 return ABS_LEFTPRESSED
;
1867 case kScrollbarRightArrow
:
1870 return ABS_RIGHTDISABLED
;
1872 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1873 return base::win::GetVersion() < base::win::VERSION_VISTA
?
1874 ABS_RIGHTHOT
: ABS_RIGHTHOVER
;
1876 return ABS_RIGHTNORMAL
;
1878 return ABS_RIGHTPRESSED
;
1884 case kScrollbarUpArrow
:
1887 return ABS_UPDISABLED
;
1889 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1890 return base::win::GetVersion() < base::win::VERSION_VISTA
?
1891 ABS_UPHOT
: ABS_UPHOVER
;
1893 return ABS_UPNORMAL
;
1895 return ABS_UPPRESSED
;
1901 case kScrollbarHorizontalThumb
:
1902 case kScrollbarVerticalThumb
:
1905 return SCRBS_DISABLED
;
1907 // Mimic WebKit's behaviour in ScrollbarThemeChromiumWin.cpp.
1908 return base::win::GetVersion() < base::win::VERSION_VISTA
?
1909 SCRBS_HOT
: SCRBS_HOVER
;
1911 return SCRBS_NORMAL
;
1913 return SCRBS_PRESSED
;
1918 case kWindowResizeGripper
:
1924 return 1; // gripper has no windows state
1929 case kComboboxArrow
:
1930 case kInnerSpinButton
:
1932 case kMenuCheckBackground
:
1933 case kMenuPopupBackground
:
1934 case kMenuItemBackground
:
1936 case kScrollbarHorizontalTrack
:
1937 case kScrollbarVerticalTrack
:
1938 case kScrollbarHorizontalGripper
:
1939 case kScrollbarVerticalGripper
:
1940 case kScrollbarCorner
:
1943 case kTabPanelBackground
:
1945 case kTrackbarThumb
:
1946 case kTrackbarTrack
:
1953 HRESULT
NativeThemeWin::GetThemeInt(ThemeName theme
,
1958 HANDLE handle
= GetThemeHandle(theme
);
1959 return (handle
&& get_theme_int_
) ?
1960 get_theme_int_(handle
, part_id
, state_id
, prop_id
, value
) : E_NOTIMPL
;
1963 HRESULT
NativeThemeWin::PaintFrameControl(HDC hdc
,
1964 const gfx::Rect
& rect
,
1968 State control_state
) const {
1969 const int width
= rect
.width();
1970 const int height
= rect
.height();
1972 // DrawFrameControl for menu arrow/check wants a monochrome bitmap.
1973 base::win::ScopedBitmap
mask_bitmap(CreateBitmap(width
, height
, 1, 1, NULL
));
1975 if (mask_bitmap
== NULL
)
1976 return E_OUTOFMEMORY
;
1978 base::win::ScopedCreateDC
bitmap_dc(CreateCompatibleDC(NULL
));
1979 base::win::ScopedSelectObject
select_bitmap(bitmap_dc
, mask_bitmap
);
1980 RECT local_rect
= { 0, 0, width
, height
};
1981 DrawFrameControl(bitmap_dc
, &local_rect
, type
, state
);
1983 // We're going to use BitBlt with a b&w mask. This results in using the dest
1984 // dc's text color for the black bits in the mask, and the dest dc's
1985 // background color for the white bits in the mask. DrawFrameControl draws the
1986 // check in black, and the background in white.
1987 int bg_color_key
= COLOR_MENU
;
1988 int text_color_key
= COLOR_MENUTEXT
;
1989 switch (control_state
) {
1991 bg_color_key
= is_selected
? COLOR_HIGHLIGHT
: COLOR_MENU
;
1992 text_color_key
= COLOR_GRAYTEXT
;
1995 bg_color_key
= COLOR_HIGHLIGHT
;
1996 text_color_key
= COLOR_HIGHLIGHTTEXT
;
2005 COLORREF old_bg_color
= SetBkColor(hdc
, GetSysColor(bg_color_key
));
2006 COLORREF old_text_color
= SetTextColor(hdc
, GetSysColor(text_color_key
));
2007 BitBlt(hdc
, rect
.x(), rect
.y(), width
, height
, bitmap_dc
, 0, 0, SRCCOPY
);
2008 SetBkColor(hdc
, old_bg_color
);
2009 SetTextColor(hdc
, old_text_color
);
2014 HANDLE
NativeThemeWin::GetThemeHandle(ThemeName theme_name
) const {
2015 if (!open_theme_
|| theme_name
< 0 || theme_name
>= LAST
)
2018 if (theme_handles_
[theme_name
])
2019 return theme_handles_
[theme_name
];
2021 // Not found, try to load it.
2023 switch (theme_name
) {
2025 handle
= open_theme_(NULL
, L
"Button");
2028 handle
= open_theme_(NULL
, L
"Listview");
2031 handle
= open_theme_(NULL
, L
"Menu");
2034 handle
= open_theme_(NULL
, L
"Combobox");
2037 handle
= open_theme_(NULL
, L
"Scrollbar");
2040 handle
= open_theme_(NULL
, L
"Status");
2043 handle
= open_theme_(NULL
, L
"Tab");
2046 handle
= open_theme_(NULL
, L
"Edit");
2049 handle
= open_theme_(NULL
, L
"Trackbar");
2052 handle
= open_theme_(NULL
, L
"Window");
2055 handle
= open_theme_(NULL
, L
"Progress");
2058 handle
= open_theme_(NULL
, L
"Spin");
2064 theme_handles_
[theme_name
] = handle
;