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/native_theme/common_theme.h"
33 // TODO: Obtain the correct colors using GetSysColor.
34 // Theme colors returned by GetSystemColor().
35 const SkColor kInvalidColorIdColor
= SkColorSetRGB(255, 0, 128);
37 const SkColor kDialogBackgroundColor
= SkColorSetRGB(251, 251, 251);
39 const SkColor kFocusedBorderColor
= SkColorSetRGB(0x4d, 0x90, 0xfe);
40 const SkColor kUnfocusedBorderColor
= SkColorSetRGB(0xd9, 0xd9, 0xd9);
42 const SkColor kTextButtonBackgroundColor
= SkColorSetRGB(0xde, 0xde, 0xde);
43 const SkColor kTextButtonHighlightColor
= SkColorSetARGB(200, 255, 255, 255);
44 const SkColor kTextButtonHoverColor
= SkColorSetRGB(6, 45, 117);
46 const SkColor kEnabledMenuItemForegroundColor
= SkColorSetRGB(6, 45, 117);
47 const SkColor kDisabledMenuItemForegroundColor
= SkColorSetRGB(161, 161, 146);
48 const SkColor kFocusedMenuItemBackgroundColor
= SkColorSetRGB(246, 249, 253);
49 const SkColor kMenuSeparatorColor
= SkColorSetARGB(50, 0, 0, 0);
51 const SkColor kTextfieldSelectionBackgroundUnfocused
= SK_ColorLTGRAY
;
53 // Windows system color IDs cached and updated by the native theme.
54 const int kSystemColors
[] = {
65 void SetCheckerboardShader(SkPaint
* paint
, const RECT
& align_rect
) {
66 // Create a 2x2 checkerboard pattern using the 3D face and highlight colors.
67 const SkColor face
= color_utils::GetSysSkColor(COLOR_3DFACE
);
68 const SkColor highlight
= color_utils::GetSysSkColor(COLOR_3DHILIGHT
);
69 SkColor buffer
[] = { face
, highlight
, highlight
, face
};
70 // Confusing bit: we first create a temporary bitmap with our desired pattern,
71 // then copy it to another bitmap. The temporary bitmap doesn't take
72 // ownership of the pixel data, and so will point to garbage when this
73 // function returns. The copy will copy the pixel data into a place owned by
74 // the bitmap, which is in turn owned by the shader, etc., so it will live
75 // until we're done using it.
77 temp_bitmap
.setConfig(SkBitmap::kARGB_8888_Config
, 2, 2);
78 temp_bitmap
.setPixels(buffer
);
80 temp_bitmap
.copyTo(&bitmap
, temp_bitmap
.config());
81 skia::RefPtr
<SkShader
> shader
= skia::AdoptRef(
82 SkShader::CreateBitmapShader(
83 bitmap
, SkShader::kRepeat_TileMode
, SkShader::kRepeat_TileMode
));
85 // Align the pattern with the upper corner of |align_rect|.
87 matrix
.setTranslate(SkIntToScalar(align_rect
.left
),
88 SkIntToScalar(align_rect
.top
));
89 shader
->setLocalMatrix(matrix
);
90 paint
->setShader(shader
.get());
96 // <-a-> <------b----->
99 // *: animating object
101 // - the animation goes from "[" to "]" repeatedly.
102 // - the animation offset is at first "|"
104 int ComputeAnimationProgress(int frame_width
,
106 int pixels_per_second
,
107 double animated_seconds
) {
108 int animation_width
= frame_width
+ object_width
;
109 double interval
= static_cast<double>(animation_width
) / pixels_per_second
;
110 double ratio
= fmod(animated_seconds
, interval
) / interval
;
111 return static_cast<int>(animation_width
* ratio
) - object_width
;
114 RECT
InsetRect(const RECT
* rect
, int size
) {
115 gfx::Rect
result(*rect
);
116 result
.Inset(size
, size
);
117 return result
.ToRECT();
124 bool NativeThemeWin::IsThemingActive() const {
125 if (is_theme_active_
)
126 return !!is_theme_active_();
130 HRESULT
NativeThemeWin::GetThemeColor(ThemeName theme
,
134 SkColor
* color
) const {
135 HANDLE handle
= GetThemeHandle(theme
);
136 if (handle
&& get_theme_color_
) {
138 if (get_theme_color_(handle
, part_id
, state_id
, prop_id
, &color_ref
) ==
140 *color
= skia::COLORREFToSkColor(color_ref
);
147 SkColor
NativeThemeWin::GetThemeColorWithDefault(ThemeName theme
,
151 int default_sys_color
) const {
153 if (GetThemeColor(theme
, part_id
, state_id
, prop_id
, &color
) != S_OK
)
154 color
= color_utils::GetSysSkColor(default_sys_color
);
158 gfx::Size
NativeThemeWin::GetThemeBorderSize(ThemeName theme
) const {
159 // For simplicity use the wildcard state==0, part==0, since it works
160 // for the cases we currently depend on.
162 if (GetThemeInt(theme
, 0, 0, TMT_BORDERSIZE
, &border
) == S_OK
)
163 return gfx::Size(border
, border
);
165 return gfx::Size(GetSystemMetrics(SM_CXEDGE
), GetSystemMetrics(SM_CYEDGE
));
168 void NativeThemeWin::DisableTheming() const {
169 if (!set_theme_properties_
)
171 set_theme_properties_(0);
174 void NativeThemeWin::CloseHandles() const {
178 for (int i
= 0; i
< LAST
; ++i
) {
179 if (theme_handles_
[i
]) {
180 close_theme_(theme_handles_
[i
]);
181 theme_handles_
[i
] = NULL
;
186 bool NativeThemeWin::IsClassicTheme(ThemeName name
) const {
190 return !GetThemeHandle(name
);
193 // TODO(sky): seems like we should default to NativeThemeWin, but that currently
194 // breaks a couple of tests (FocusTraversalTest.NormalTraversal in
196 #if !defined(USE_AURA)
198 NativeTheme
* NativeTheme::instance() {
199 return NativeThemeWin::instance();
204 NativeThemeWin
* NativeThemeWin::instance() {
205 CR_DEFINE_STATIC_LOCAL(NativeThemeWin
, s_native_theme
, ());
206 return &s_native_theme
;
209 gfx::Size
NativeThemeWin::GetPartSize(Part part
,
211 const ExtraParams
& extra
) const {
212 if (IsNewMenuStyleEnabled()) {
213 gfx::Size size
= CommonThemeGetPartSize(part
, state
, extra
);
218 int part_id
= GetWindowsPart(part
, state
, extra
);
219 int state_id
= GetWindowsState(part
, state
, extra
);
221 HDC hdc
= GetDC(NULL
);
222 HRESULT hr
= GetThemePartSize(GetThemeName(part
), hdc
, part_id
, state_id
,
223 NULL
, TS_TRUE
, &size
);
224 ReleaseDC(NULL
, hdc
);
227 // TODO(rogerta): For now, we need to support radio buttons and checkboxes
228 // when theming is not enabled. Support for other parts can be added
233 // TODO(rogerta): I was not able to find any API to get the default
234 // size of these controls, so determined these values empirically.
245 return gfx::Size(size
.cx
, size
.cy
);
248 void NativeThemeWin::Paint(SkCanvas
* canvas
,
251 const gfx::Rect
& rect
,
252 const ExtraParams
& extra
) const {
253 if (IsNewMenuStyleEnabled()) {
255 case kMenuPopupGutter
:
256 CommonThemePaintMenuGutter(canvas
, rect
);
258 case kMenuPopupSeparator
:
259 CommonThemePaintMenuSeparator(canvas
, rect
, extra
.menu_separator
);
261 case kMenuPopupBackground
:
262 CommonThemePaintMenuBackground(canvas
, rect
);
264 case kMenuItemBackground
:
265 CommonThemePaintMenuItemBackground(canvas
, state
, rect
);
270 bool needs_paint_indirect
= false;
271 if (!skia::SupportsPlatformPaint(canvas
)) {
272 // This block will only get hit with --enable-accelerated-drawing flag.
273 needs_paint_indirect
= true;
275 // Scrollbars on Windows XP and the Windows Classic theme have particularly
276 // problematic alpha values, so always draw them indirectly.
278 case kScrollbarDownArrow
:
279 case kScrollbarUpArrow
:
280 case kScrollbarLeftArrow
:
281 case kScrollbarRightArrow
:
282 case kScrollbarHorizontalThumb
:
283 case kScrollbarVerticalThumb
:
284 case kScrollbarHorizontalGripper
:
285 case kScrollbarVerticalGripper
:
286 if (!GetThemeHandle(SCROLLBAR
))
287 needs_paint_indirect
= true;
294 if (needs_paint_indirect
)
295 PaintIndirect(canvas
, part
, state
, rect
, extra
);
297 PaintDirect(canvas
, part
, state
, rect
, extra
);
300 NativeThemeWin::NativeThemeWin()
301 : theme_dll_(LoadLibrary(L
"uxtheme.dll")),
303 draw_theme_ex_(NULL
),
304 get_theme_color_(NULL
),
305 get_theme_content_rect_(NULL
),
306 get_theme_part_size_(NULL
),
309 set_theme_properties_(NULL
),
310 is_theme_active_(NULL
),
311 get_theme_int_(NULL
),
312 ALLOW_THIS_IN_INITIALIZER_LIST(color_change_listener_(this)) {
314 draw_theme_
= reinterpret_cast<DrawThemeBackgroundPtr
>(
315 GetProcAddress(theme_dll_
, "DrawThemeBackground"));
316 draw_theme_ex_
= reinterpret_cast<DrawThemeBackgroundExPtr
>(
317 GetProcAddress(theme_dll_
, "DrawThemeBackgroundEx"));
318 get_theme_color_
= reinterpret_cast<GetThemeColorPtr
>(
319 GetProcAddress(theme_dll_
, "GetThemeColor"));
320 get_theme_content_rect_
= reinterpret_cast<GetThemeContentRectPtr
>(
321 GetProcAddress(theme_dll_
, "GetThemeBackgroundContentRect"));
322 get_theme_part_size_
= reinterpret_cast<GetThemePartSizePtr
>(
323 GetProcAddress(theme_dll_
, "GetThemePartSize"));
324 open_theme_
= reinterpret_cast<OpenThemeDataPtr
>(
325 GetProcAddress(theme_dll_
, "OpenThemeData"));
326 close_theme_
= reinterpret_cast<CloseThemeDataPtr
>(
327 GetProcAddress(theme_dll_
, "CloseThemeData"));
328 set_theme_properties_
= reinterpret_cast<SetThemeAppPropertiesPtr
>(
329 GetProcAddress(theme_dll_
, "SetThemeAppProperties"));
330 is_theme_active_
= reinterpret_cast<IsThemeActivePtr
>(
331 GetProcAddress(theme_dll_
, "IsThemeActive"));
332 get_theme_int_
= reinterpret_cast<GetThemeIntPtr
>(
333 GetProcAddress(theme_dll_
, "GetThemeInt"));
335 memset(theme_handles_
, 0, sizeof(theme_handles_
));
337 // Initialize the cached system colors.
338 UpdateSystemColors();
341 NativeThemeWin::~NativeThemeWin() {
343 // todo (cpu): fix this soon. Making a call to CloseHandles() here breaks
344 // certain tests and the reliability bots.
346 FreeLibrary(theme_dll_
);
350 void NativeThemeWin::OnSysColorChange() {
351 UpdateSystemColors();
354 void NativeThemeWin::UpdateSystemColors() {
355 for (int i
= 0; i
< arraysize(kSystemColors
); ++i
) {
356 system_colors_
[kSystemColors
[i
]] =
357 color_utils::GetSysSkColor(kSystemColors
[i
]);
361 void NativeThemeWin::PaintDirect(SkCanvas
* canvas
,
364 const gfx::Rect
& rect
,
365 const ExtraParams
& extra
) const {
366 skia::ScopedPlatformPaint
scoped_platform_paint(canvas
);
367 HDC hdc
= scoped_platform_paint
.GetPlatformSurface();
371 PaintCheckbox(hdc
, part
, state
, rect
, extra
.button
);
374 PaintRadioButton(hdc
, part
, state
, rect
, extra
.button
);
377 PaintPushButton(hdc
, part
, state
, rect
, extra
.button
);
379 case kMenuPopupArrow
:
380 PaintMenuArrow(hdc
, state
, rect
, extra
.menu_arrow
);
382 case kMenuPopupGutter
:
383 PaintMenuGutter(hdc
, rect
);
385 case kMenuPopupSeparator
:
386 PaintMenuSeparator(hdc
, rect
, extra
.menu_separator
);
388 case kMenuPopupBackground
:
389 PaintMenuBackground(hdc
, rect
);
392 PaintMenuCheck(hdc
, state
, rect
, extra
.menu_check
);
394 case kMenuCheckBackground
:
395 PaintMenuCheckBackground(hdc
, state
, rect
);
397 case kMenuItemBackground
:
398 PaintMenuItemBackground(hdc
, state
, rect
, extra
.menu_item
);
401 PaintMenuList(hdc
, state
, rect
, extra
.menu_list
);
403 case kScrollbarDownArrow
:
404 case kScrollbarUpArrow
:
405 case kScrollbarLeftArrow
:
406 case kScrollbarRightArrow
:
407 PaintScrollbarArrow(hdc
, part
, state
, rect
, extra
.scrollbar_arrow
);
409 case kScrollbarHorizontalTrack
:
410 case kScrollbarVerticalTrack
:
411 PaintScrollbarTrack(canvas
, hdc
, part
, state
, rect
,
412 extra
.scrollbar_track
);
414 case kScrollbarHorizontalThumb
:
415 case kScrollbarVerticalThumb
:
416 case kScrollbarHorizontalGripper
:
417 case kScrollbarVerticalGripper
:
418 PaintScrollbarThumb(hdc
, part
, state
, rect
, extra
.scrollbar_thumb
);
420 case kInnerSpinButton
:
421 PaintSpinButton(hdc
, part
, state
, rect
, extra
.inner_spin
);
425 PaintTrackbar(canvas
, hdc
, part
, state
, rect
, extra
.trackbar
);
428 PaintProgressBar(hdc
, rect
, extra
.progress_bar
);
430 case kWindowResizeGripper
:
431 PaintWindowResizeGripper(hdc
, rect
);
433 case kTabPanelBackground
:
434 PaintTabPanelBackground(hdc
, rect
);
437 PaintTextField(hdc
, part
, state
, rect
, extra
.text_field
);
443 // While transitioning NativeThemeWin to the single Paint() entry point,
444 // unsupported parts will DCHECK here.
449 SkColor
NativeThemeWin::GetSystemColor(ColorId color_id
) const {
451 if (IsNewMenuStyleEnabled() &&
452 CommonThemeGetSystemColor(color_id
, &color
)) {
458 case kColorId_WindowBackground
:
459 return system_colors_
[COLOR_WINDOW
];
462 case kColorId_DialogBackground
:
463 return kDialogBackgroundColor
;
466 case kColorId_FocusedBorderColor
:
467 return kFocusedBorderColor
;
468 case kColorId_UnfocusedBorderColor
:
469 return kUnfocusedBorderColor
;
472 case kColorId_TextButtonBackgroundColor
:
473 return kTextButtonBackgroundColor
;
474 case kColorId_TextButtonEnabledColor
:
475 return system_colors_
[COLOR_BTNTEXT
];
476 case kColorId_TextButtonDisabledColor
:
477 return system_colors_
[COLOR_GRAYTEXT
];
478 case kColorId_TextButtonHighlightColor
:
479 return kTextButtonHighlightColor
;
480 case kColorId_TextButtonHoverColor
:
481 return kTextButtonHoverColor
;
484 case kColorId_EnabledMenuItemForegroundColor
:
485 return kEnabledMenuItemForegroundColor
;
486 case kColorId_DisabledMenuItemForegroundColor
:
487 return kDisabledMenuItemForegroundColor
;
488 case kColorId_FocusedMenuItemBackgroundColor
:
489 return kFocusedMenuItemBackgroundColor
;
490 case kColorId_MenuSeparatorColor
:
491 return kMenuSeparatorColor
;
494 case kColorId_LabelEnabledColor
:
495 return system_colors_
[COLOR_BTNTEXT
];
496 case kColorId_LabelDisabledColor
:
497 return system_colors_
[COLOR_GRAYTEXT
];
498 case kColorId_LabelBackgroundColor
:
499 return system_colors_
[COLOR_WINDOW
];
502 case kColorId_TextfieldDefaultColor
:
503 return system_colors_
[COLOR_WINDOWTEXT
];
504 case kColorId_TextfieldDefaultBackground
:
505 return system_colors_
[COLOR_WINDOW
];
506 case kColorId_TextfieldReadOnlyColor
:
507 return system_colors_
[COLOR_GRAYTEXT
];
508 case kColorId_TextfieldReadOnlyBackground
:
509 return system_colors_
[COLOR_3DFACE
];
510 case kColorId_TextfieldSelectionColor
:
511 return system_colors_
[COLOR_HIGHLIGHTTEXT
];
512 case kColorId_TextfieldSelectionBackgroundFocused
:
513 return system_colors_
[COLOR_HIGHLIGHT
];
514 case kColorId_TextfieldSelectionBackgroundUnfocused
:
515 return kTextfieldSelectionBackgroundUnfocused
;
518 NOTREACHED() << "Invalid color_id: " << color_id
;
521 return kInvalidColorIdColor
;
524 void NativeThemeWin::PaintIndirect(SkCanvas
* canvas
,
527 const gfx::Rect
& rect
,
528 const ExtraParams
& extra
) const {
529 // TODO(asvitkine): This path is pretty inefficient - for each paint operation
530 // it creates a new offscreen bitmap Skia canvas. This can
531 // be sped up by doing it only once per part/state and
532 // keeping a cache of the resulting bitmaps.
534 // Create an offscreen canvas that is backed by an HDC.
535 skia::RefPtr
<skia::BitmapPlatformDevice
> device
= skia::AdoptRef(
536 skia::BitmapPlatformDevice::Create(
537 rect
.width(), rect
.height(), false, NULL
));
539 SkCanvas
offscreen_canvas(device
.get());
540 DCHECK(skia::SupportsPlatformPaint(&offscreen_canvas
));
542 // Some of the Windows theme drawing operations do not write correct alpha
543 // values for fully-opaque pixels; instead the pixels get alpha 0. This is
544 // especially a problem on Windows XP or when using the Classic theme.
546 // To work-around this, mark all pixels with a placeholder value, to detect
547 // which pixels get touched by the paint operation. After paint, set any
548 // pixels that have alpha 0 to opaque and placeholders to fully-transparent.
549 const SkColor placeholder
= SkColorSetARGB(1, 0, 0, 0);
550 offscreen_canvas
.clear(placeholder
);
552 // Offset destination rects to have origin (0,0).
553 gfx::Rect
adjusted_rect(rect
.size());
554 ExtraParams
adjusted_extra(extra
);
557 adjusted_extra
.progress_bar
.value_rect_x
= 0;
558 adjusted_extra
.progress_bar
.value_rect_y
= 0;
560 case kScrollbarHorizontalTrack
:
561 case kScrollbarVerticalTrack
:
562 adjusted_extra
.scrollbar_track
.track_x
= 0;
563 adjusted_extra
.scrollbar_track
.track_y
= 0;
567 // Draw the theme controls using existing HDC-drawing code.
568 PaintDirect(&offscreen_canvas
,
574 // Copy the pixels to a bitmap that has ref-counted pixel storage, which is
575 // necessary to have when drawing to a SkPicture.
576 const SkBitmap
& hdc_bitmap
=
577 offscreen_canvas
.getDevice()->accessBitmap(false);
579 hdc_bitmap
.copyTo(&bitmap
, SkBitmap::kARGB_8888_Config
);
581 // Post-process the pixels to fix up the alpha values (see big comment above).
582 const SkPMColor placeholder_value
= SkPreMultiplyColor(placeholder
);
583 const int pixel_count
= rect
.width() * rect
.height();
584 SkPMColor
* pixels
= bitmap
.getAddr32(0, 0);
585 for (int i
= 0; i
< pixel_count
; i
++) {
586 if (pixels
[i
] == placeholder_value
) {
587 // Pixel wasn't touched - make it fully transparent.
588 pixels
[i
] = SkPackARGB32(0, 0, 0, 0);
589 } else if (SkGetPackedA32(pixels
[i
]) == 0) {
590 // Pixel was touched but has incorrect alpha of 0, make it fully opaque.
591 pixels
[i
] = SkPackARGB32(0xFF,
592 SkGetPackedR32(pixels
[i
]),
593 SkGetPackedG32(pixels
[i
]),
594 SkGetPackedB32(pixels
[i
]));
598 // Draw the offscreen bitmap to the destination canvas.
599 canvas
->drawBitmap(bitmap
, rect
.x(), rect
.y());
602 HRESULT
NativeThemeWin::GetThemePartSize(ThemeName theme_name
,
609 HANDLE handle
= GetThemeHandle(theme_name
);
610 if (handle
&& get_theme_part_size_
)
611 return get_theme_part_size_(handle
, hdc
, part_id
, state_id
, rect
, ts
, size
);
616 HRESULT
NativeThemeWin::PaintButton(HDC hdc
,
618 const ButtonExtraParams
& extra
,
622 HANDLE handle
= GetThemeHandle(BUTTON
);
623 if (handle
&& draw_theme_
)
624 return draw_theme_(handle
, hdc
, part_id
, state_id
, rect
, NULL
);
626 // Adjust classic_state based on part, state, and extras.
627 int classic_state
= extra
.classic_state
;
630 classic_state
|= DFCS_BUTTONCHECK
;
633 classic_state
|= DFCS_BUTTONRADIO
;
636 classic_state
|= DFCS_BUTTONPUSH
;
639 NOTREACHED() << "Unknown part_id: " << part_id
;
645 classic_state
|= DFCS_INACTIVE
;
648 classic_state
|= DFCS_PUSHED
;
654 NOTREACHED() << "Unknown state: " << state
;
659 classic_state
|= DFCS_CHECKED
;
662 // All pressed states have both low bits set, and no other states do.
663 const bool focused
= ((state_id
& ETS_FOCUSED
) == ETS_FOCUSED
);
664 const bool pressed
= ((state_id
& PBS_PRESSED
) == PBS_PRESSED
);
665 if ((BP_PUSHBUTTON
== part_id
) && (pressed
|| focused
)) {
666 // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the
667 // button itself is shrunk by 1 pixel.
668 HBRUSH brush
= GetSysColorBrush(COLOR_3DDKSHADOW
);
670 FrameRect(hdc
, rect
, brush
);
671 InflateRect(rect
, -1, -1);
674 DrawFrameControl(hdc
, rect
, DFC_BUTTON
, classic_state
);
676 // Draw the focus rectangle (the dotted line box) only on buttons. For radio
677 // and checkboxes, we let webkit draw the focus rectangle (orange glow).
678 if ((BP_PUSHBUTTON
== part_id
) && focused
) {
679 // The focus rect is inside the button. The exact number of pixels depends
680 // on whether we're in classic mode or using uxtheme.
681 if (handle
&& get_theme_content_rect_
) {
682 get_theme_content_rect_(handle
, hdc
, part_id
, state_id
, rect
, rect
);
684 InflateRect(rect
, -GetSystemMetrics(SM_CXEDGE
),
685 -GetSystemMetrics(SM_CYEDGE
));
687 DrawFocusRect(hdc
, rect
);
693 HRESULT
NativeThemeWin::PaintMenuSeparator(
695 const gfx::Rect
& rect
,
696 const MenuSeparatorExtraParams
& extra
) const {
697 RECT rect_win
= rect
.ToRECT();
699 HANDLE handle
= GetThemeHandle(MENU
);
700 if (handle
&& draw_theme_
) {
701 // Delta is needed for non-classic to move separator up slightly.
704 return draw_theme_(handle
, hdc
, MENU_POPUPSEPARATOR
, MPI_NORMAL
, &rect_win
,
708 DrawEdge(hdc
, &rect_win
, EDGE_ETCHED
, BF_TOP
);
712 HRESULT
NativeThemeWin::PaintMenuGutter(HDC hdc
,
713 const gfx::Rect
& rect
) const {
714 RECT rect_win
= rect
.ToRECT();
715 HANDLE handle
= GetThemeHandle(MENU
);
716 if (handle
&& draw_theme_
)
717 return draw_theme_(handle
, hdc
, MENU_POPUPGUTTER
, MPI_NORMAL
, &rect_win
,
722 HRESULT
NativeThemeWin::PaintMenuArrow(HDC hdc
,
724 const gfx::Rect
& rect
,
725 const MenuArrowExtraParams
& extra
)
727 int state_id
= MSM_NORMAL
;
728 if (state
== kDisabled
)
729 state_id
= MSM_DISABLED
;
731 HANDLE handle
= GetThemeHandle(MENU
);
732 RECT rect_win
= rect
.ToRECT();
733 if (handle
&& draw_theme_
) {
734 if (extra
.pointing_right
) {
735 return draw_theme_(handle
, hdc
, MENU_POPUPSUBMENU
, state_id
, &rect_win
,
738 // There is no way to tell the uxtheme API to draw a left pointing arrow;
739 // it doesn't have a flag equivalent to DFCS_MENUARROWRIGHT. But they
740 // are needed for RTL locales on Vista. So use a memory DC and mirror
741 // the region with GDI's StretchBlt.
743 base::win::ScopedCreateDC
mem_dc(CreateCompatibleDC(hdc
));
744 base::win::ScopedBitmap
mem_bitmap(CreateCompatibleBitmap(hdc
, r
.width(),
746 base::win::ScopedSelectObject
select_bitmap(mem_dc
, mem_bitmap
);
747 // Copy and horizontally mirror the background from hdc into mem_dc. Use
748 // a negative-width source rect, starting at the rightmost pixel.
749 StretchBlt(mem_dc
, 0, 0, r
.width(), r
.height(),
750 hdc
, r
.right()-1, r
.y(), -r
.width(), r
.height(), SRCCOPY
);
752 RECT theme_rect
= {0, 0, r
.width(), r
.height()};
753 HRESULT result
= draw_theme_(handle
, mem_dc
, MENU_POPUPSUBMENU
,
754 state_id
, &theme_rect
, NULL
);
755 // Copy and mirror the result back into mem_dc.
756 StretchBlt(hdc
, r
.x(), r
.y(), r
.width(), r
.height(),
757 mem_dc
, r
.width()-1, 0, -r
.width(), r
.height(), SRCCOPY
);
762 // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
763 // left pointing arrow. This makes the following 'if' statement slightly
766 if (extra
.pointing_right
)
767 pfc_state
= DFCS_MENUARROW
;
769 pfc_state
= DFCS_MENUARROWRIGHT
;
770 return PaintFrameControl(hdc
, rect
, DFC_MENU
, pfc_state
, extra
.is_selected
,
774 HRESULT
NativeThemeWin::PaintMenuBackground(HDC hdc
,
775 const gfx::Rect
& rect
) const {
776 HANDLE handle
= GetThemeHandle(MENU
);
777 RECT rect_win
= rect
.ToRECT();
778 if (handle
&& draw_theme_
) {
779 HRESULT result
= draw_theme_(handle
, hdc
, MENU_POPUPBACKGROUND
, 0,
781 FrameRect(hdc
, &rect_win
, GetSysColorBrush(COLOR_3DSHADOW
));
785 FillRect(hdc
, &rect_win
, GetSysColorBrush(COLOR_MENU
));
786 DrawEdge(hdc
, &rect_win
, EDGE_RAISED
, BF_RECT
);
790 HRESULT
NativeThemeWin::PaintMenuCheck(
793 const gfx::Rect
& rect
,
794 const MenuCheckExtraParams
& extra
) const {
795 HANDLE handle
= GetThemeHandle(MENU
);
797 if (extra
.is_radio
) {
798 state_id
= state
== kDisabled
? MC_BULLETDISABLED
: MC_BULLETNORMAL
;
800 state_id
= state
== kDisabled
? MC_CHECKMARKDISABLED
: MC_CHECKMARKNORMAL
;
803 RECT rect_win
= rect
.ToRECT();
804 if (handle
&& draw_theme_
)
805 return draw_theme_(handle
, hdc
, MENU_POPUPCHECK
, state_id
, &rect_win
, NULL
);
807 return PaintFrameControl(hdc
, rect
, DFC_MENU
,
808 extra
.is_radio
? DFCS_MENUBULLET
: DFCS_MENUCHECK
,
809 extra
.is_selected
, state
);
812 HRESULT
NativeThemeWin::PaintMenuCheckBackground(HDC hdc
,
814 const gfx::Rect
& rect
) const {
815 HANDLE handle
= GetThemeHandle(MENU
);
816 int state_id
= state
== kDisabled
? MCB_DISABLED
: MCB_NORMAL
;
817 RECT rect_win
= rect
.ToRECT();
818 if (handle
&& draw_theme_
)
819 return draw_theme_(handle
, hdc
, MENU_POPUPCHECKBACKGROUND
, state_id
,
821 // Nothing to do for background.
825 HRESULT
NativeThemeWin::PaintMenuItemBackground(
828 const gfx::Rect
& rect
,
829 const MenuItemExtraParams
& extra
) const {
830 HANDLE handle
= GetThemeHandle(MENU
);
831 RECT rect_win
= rect
.ToRECT();
835 state_id
= MPI_NORMAL
;
838 state_id
= extra
.is_selected
? MPI_DISABLEDHOT
: MPI_DISABLED
;
844 NOTREACHED() << "Invalid state " << state
;
848 if (handle
&& draw_theme_
)
849 return draw_theme_(handle
, hdc
, MENU_POPUPITEM
, state_id
, &rect_win
, NULL
);
851 if (extra
.is_selected
)
852 FillRect(hdc
, &rect_win
, GetSysColorBrush(COLOR_HIGHLIGHT
));
856 HRESULT
NativeThemeWin::PaintPushButton(HDC hdc
,
859 const gfx::Rect
& rect
,
860 const ButtonExtraParams
& extra
) const {
864 state_id
= PBS_DISABLED
;
870 state_id
= extra
.is_default
? PBS_DEFAULTED
: PBS_NORMAL
;
873 state_id
= PBS_PRESSED
;
876 NOTREACHED() << "Invalid state: " << state
;
880 RECT rect_win
= rect
.ToRECT();
881 return PaintButton(hdc
, state
, extra
, BP_PUSHBUTTON
, state_id
, &rect_win
);
884 HRESULT
NativeThemeWin::PaintRadioButton(HDC hdc
,
887 const gfx::Rect
& rect
,
888 const ButtonExtraParams
& extra
) const {
892 state_id
= extra
.checked
? RBS_CHECKEDDISABLED
: RBS_UNCHECKEDDISABLED
;
895 state_id
= extra
.checked
? RBS_CHECKEDHOT
: RBS_UNCHECKEDHOT
;
898 state_id
= extra
.checked
? RBS_CHECKEDNORMAL
: RBS_UNCHECKEDNORMAL
;
901 state_id
= extra
.checked
? RBS_CHECKEDPRESSED
: RBS_UNCHECKEDPRESSED
;
904 NOTREACHED() << "Invalid state: " << state
;
908 RECT rect_win
= rect
.ToRECT();
909 return PaintButton(hdc
, state
, extra
, BP_RADIOBUTTON
, state_id
, &rect_win
);
912 HRESULT
NativeThemeWin::PaintCheckbox(HDC hdc
,
915 const gfx::Rect
& rect
,
916 const ButtonExtraParams
& extra
) const {
920 state_id
= extra
.checked
? CBS_CHECKEDDISABLED
:
921 extra
.indeterminate
? CBS_MIXEDDISABLED
:
922 CBS_UNCHECKEDDISABLED
;
925 state_id
= extra
.checked
? CBS_CHECKEDHOT
:
926 extra
.indeterminate
? CBS_MIXEDHOT
:
930 state_id
= extra
.checked
? CBS_CHECKEDNORMAL
:
931 extra
.indeterminate
? CBS_MIXEDNORMAL
:
935 state_id
= extra
.checked
? CBS_CHECKEDPRESSED
:
936 extra
.indeterminate
? CBS_MIXEDPRESSED
:
937 CBS_UNCHECKEDPRESSED
;
940 NOTREACHED() << "Invalid state: " << state
;
944 RECT rect_win
= rect
.ToRECT();
945 return PaintButton(hdc
, state
, extra
, BP_CHECKBOX
, state_id
, &rect_win
);
948 HRESULT
NativeThemeWin::PaintMenuList(HDC hdc
,
950 const gfx::Rect
& rect
,
951 const MenuListExtraParams
& extra
) const {
952 HANDLE handle
= GetThemeHandle(MENULIST
);
953 RECT rect_win
= rect
.ToRECT();
957 state_id
= CBXS_NORMAL
;
960 state_id
= CBXS_DISABLED
;
966 state_id
= CBXS_PRESSED
;
969 NOTREACHED() << "Invalid state " << state
;
973 if (handle
&& draw_theme_
)
974 return draw_theme_(handle
, hdc
, CP_DROPDOWNBUTTON
, state_id
, &rect_win
,
978 DrawFrameControl(hdc
, &rect_win
, DFC_SCROLL
,
979 DFCS_SCROLLCOMBOBOX
| extra
.classic_state
);
983 HRESULT
NativeThemeWin::PaintScrollbarArrow(
987 const gfx::Rect
& rect
,
988 const ScrollbarArrowExtraParams
& extra
) const {
989 static const int state_id_matrix
[4][kMaxState
] = {
990 ABS_DOWNDISABLED
, ABS_DOWNHOT
, ABS_DOWNNORMAL
, ABS_DOWNPRESSED
,
991 ABS_LEFTDISABLED
, ABS_LEFTHOT
, ABS_LEFTNORMAL
, ABS_LEFTPRESSED
,
992 ABS_RIGHTDISABLED
, ABS_RIGHTHOT
, ABS_RIGHTNORMAL
, ABS_RIGHTPRESSED
,
993 ABS_UPDISABLED
, ABS_UPHOT
, ABS_UPNORMAL
, ABS_UPPRESSED
995 HANDLE handle
= GetThemeHandle(SCROLLBAR
);
996 RECT rect_win
= rect
.ToRECT();
997 if (handle
&& draw_theme_
) {
998 int index
= part
- kScrollbarDownArrow
;
999 DCHECK(index
>=0 && index
< 4);
1000 int state_id
= state_id_matrix
[index
][state
];
1002 // Hovering means that the cursor is over the scroolbar, but not over the
1003 // specific arrow itself. We don't want to show it "hot" mode, but only
1005 if (state
== kHovered
&& extra
.is_hovering
) {
1007 case kScrollbarDownArrow
:
1008 state_id
= ABS_DOWNHOVER
;
1010 case kScrollbarLeftArrow
:
1011 state_id
= ABS_LEFTHOVER
;
1013 case kScrollbarRightArrow
:
1014 state_id
= ABS_RIGHTHOVER
;
1016 case kScrollbarUpArrow
:
1017 state_id
= ABS_UPHOVER
;
1020 NOTREACHED() << "Invalid part: " << part
;
1025 return draw_theme_(handle
, hdc
, SBP_ARROWBTN
, state_id
, &rect_win
, NULL
);
1028 int classic_state
= DFCS_SCROLLDOWN
;
1030 case kScrollbarDownArrow
:
1031 classic_state
= DFCS_SCROLLDOWN
;
1033 case kScrollbarLeftArrow
:
1034 classic_state
= DFCS_SCROLLLEFT
;
1036 case kScrollbarRightArrow
:
1037 classic_state
= DFCS_SCROLLRIGHT
;
1039 case kScrollbarUpArrow
:
1040 classic_state
= DFCS_SCROLLUP
;
1043 NOTREACHED() << "Invalid part: " << part
;
1048 classic_state
|= DFCS_INACTIVE
;
1051 classic_state
|= DFCS_HOT
;
1056 classic_state
|= DFCS_PUSHED
;
1059 NOTREACHED() << "Invalid state: " << state
;
1062 DrawFrameControl(hdc
, &rect_win
, DFC_SCROLL
, classic_state
);
1066 HRESULT
NativeThemeWin::PaintScrollbarThumb(
1070 const gfx::Rect
& rect
,
1071 const ScrollbarThumbExtraParams
& extra
) const {
1072 HANDLE handle
= GetThemeHandle(SCROLLBAR
);
1073 RECT rect_win
= rect
.ToRECT();
1078 case NativeTheme::kScrollbarHorizontalThumb
:
1079 part_id
= SBP_THUMBBTNHORZ
;
1081 case NativeTheme::kScrollbarVerticalThumb
:
1082 part_id
= SBP_THUMBBTNVERT
;
1084 case NativeTheme::kScrollbarHorizontalGripper
:
1085 part_id
= SBP_GRIPPERHORZ
;
1087 case NativeTheme::kScrollbarVerticalGripper
:
1088 part_id
= SBP_GRIPPERVERT
;
1091 NOTREACHED() << "Invalid part: " << part
;
1097 state_id
= SCRBS_DISABLED
;
1100 state_id
= extra
.is_hovering
? SCRBS_HOVER
: SCRBS_HOT
;
1103 state_id
= SCRBS_NORMAL
;
1106 state_id
= SCRBS_PRESSED
;
1109 NOTREACHED() << "Invalid state: " << state
;
1113 if (handle
&& draw_theme_
)
1114 return draw_theme_(handle
, hdc
, part_id
, state_id
, &rect_win
, NULL
);
1116 // Draw it manually.
1117 if ((part_id
== SBP_THUMBBTNHORZ
) || (part_id
== SBP_THUMBBTNVERT
))
1118 DrawEdge(hdc
, &rect_win
, EDGE_RAISED
, BF_RECT
| BF_MIDDLE
);
1119 // Classic mode doesn't have a gripper.
1123 HRESULT
NativeThemeWin::PaintScrollbarTrack(
1128 const gfx::Rect
& rect
,
1129 const ScrollbarTrackExtraParams
& extra
) const {
1130 HANDLE handle
= GetThemeHandle(SCROLLBAR
);
1131 RECT rect_win
= rect
.ToRECT();
1136 case NativeTheme::kScrollbarHorizontalTrack
:
1137 part_id
= extra
.is_upper
? SBP_UPPERTRACKHORZ
: SBP_LOWERTRACKHORZ
;
1139 case NativeTheme::kScrollbarVerticalTrack
:
1140 part_id
= extra
.is_upper
? SBP_UPPERTRACKVERT
: SBP_LOWERTRACKVERT
;
1143 NOTREACHED() << "Invalid part: " << part
;
1149 state_id
= SCRBS_DISABLED
;
1152 state_id
= SCRBS_HOVER
;
1155 state_id
= SCRBS_NORMAL
;
1158 state_id
= SCRBS_PRESSED
;
1161 NOTREACHED() << "Invalid state: " << state
;
1165 if (handle
&& draw_theme_
)
1166 return draw_theme_(handle
, hdc
, part_id
, state_id
, &rect_win
, NULL
);
1168 // Draw it manually.
1169 if ((system_colors_
[COLOR_SCROLLBAR
] != system_colors_
[COLOR_3DFACE
]) &&
1170 (system_colors_
[COLOR_SCROLLBAR
] != system_colors_
[COLOR_WINDOW
])) {
1171 FillRect(hdc
, &rect_win
, reinterpret_cast<HBRUSH
>(COLOR_SCROLLBAR
+ 1));
1174 RECT align_rect
= gfx::Rect(extra
.track_x
, extra
.track_y
, extra
.track_width
,
1175 extra
.track_height
).ToRECT();
1176 SetCheckerboardShader(&paint
, align_rect
);
1177 canvas
->drawIRect(skia::RECTToSkIRect(rect_win
), paint
);
1179 if (extra
.classic_state
& DFCS_PUSHED
)
1180 InvertRect(hdc
, &rect_win
);
1184 HRESULT
NativeThemeWin::PaintSpinButton(
1188 const gfx::Rect
& rect
,
1189 const InnerSpinButtonExtraParams
& extra
) const {
1190 HANDLE handle
= GetThemeHandle(SPIN
);
1191 RECT rect_win
= rect
.ToRECT();
1192 int part_id
= extra
.spin_up
? SPNP_UP
: SPNP_DOWN
;
1196 state_id
= extra
.spin_up
? UPS_DISABLED
: DNS_DISABLED
;
1199 state_id
= extra
.spin_up
? UPS_HOT
: DNS_HOT
;
1202 state_id
= extra
.spin_up
? UPS_NORMAL
: DNS_NORMAL
;
1205 state_id
= extra
.spin_up
? UPS_PRESSED
: DNS_PRESSED
;
1208 NOTREACHED() << "Invalid state " << state
;
1212 if (handle
&& draw_theme_
)
1213 return draw_theme_(handle
, hdc
, part_id
, state_id
, &rect_win
, NULL
);
1214 DrawFrameControl(hdc
, &rect_win
, DFC_SCROLL
, extra
.classic_state
);
1218 HRESULT
NativeThemeWin::PaintTrackbar(
1223 const gfx::Rect
& rect
,
1224 const TrackbarExtraParams
& extra
) const {
1225 int part_id
= part
== kTrackbarTrack
? TKP_TRACK
: TKP_THUMBBOTTOM
;
1227 part_id
= part
== kTrackbarTrack
? TKP_TRACKVERT
: TKP_THUMBVERT
;
1232 state_id
= TUS_DISABLED
;
1238 state_id
= TUS_NORMAL
;
1241 state_id
= TUS_PRESSED
;
1244 NOTREACHED() << "Invalid state " << state
;
1248 // Make the channel be 4 px thick in the center of the supplied rect. (4 px
1249 // matches what XP does in various menus; GetThemePartSize() doesn't seem to
1250 // return good values here.)
1251 RECT rect_win
= rect
.ToRECT();
1252 RECT channel_rect
= rect
.ToRECT();
1253 const int channel_thickness
= 4;
1254 if (part_id
== TKP_TRACK
) {
1256 ((channel_rect
.bottom
- channel_rect
.top
- channel_thickness
) / 2);
1257 channel_rect
.bottom
= channel_rect
.top
+ channel_thickness
;
1258 } else if (part_id
== TKP_TRACKVERT
) {
1259 channel_rect
.left
+=
1260 ((channel_rect
.right
- channel_rect
.left
- channel_thickness
) / 2);
1261 channel_rect
.right
= channel_rect
.left
+ channel_thickness
;
1262 } // else this isn't actually a channel, so |channel_rect| == |rect|.
1264 HANDLE handle
= GetThemeHandle(TRACKBAR
);
1265 if (handle
&& draw_theme_
)
1266 return draw_theme_(handle
, hdc
, part_id
, state_id
, &channel_rect
, NULL
);
1268 // Classic mode, draw it manually.
1269 if ((part_id
== TKP_TRACK
) || (part_id
== TKP_TRACKVERT
)) {
1270 DrawEdge(hdc
, &channel_rect
, EDGE_SUNKEN
, BF_RECT
);
1271 } else if (part_id
== TKP_THUMBVERT
) {
1272 DrawEdge(hdc
, &rect_win
, EDGE_RAISED
, BF_RECT
| BF_SOFT
| BF_MIDDLE
);
1274 // Split rect into top and bottom pieces.
1275 RECT top_section
= rect
.ToRECT();
1276 RECT bottom_section
= rect
.ToRECT();
1277 top_section
.bottom
-= ((bottom_section
.right
- bottom_section
.left
) / 2);
1278 bottom_section
.top
= top_section
.bottom
;
1279 DrawEdge(hdc
, &top_section
, EDGE_RAISED
,
1280 BF_LEFT
| BF_TOP
| BF_RIGHT
| BF_SOFT
| BF_MIDDLE
| BF_ADJUST
);
1282 // Split triangular piece into two diagonals.
1283 RECT
& left_half
= bottom_section
;
1284 RECT right_half
= bottom_section
;
1285 right_half
.left
+= ((bottom_section
.right
- bottom_section
.left
) / 2);
1286 left_half
.right
= right_half
.left
;
1287 DrawEdge(hdc
, &left_half
, EDGE_RAISED
,
1288 BF_DIAGONAL_ENDTOPLEFT
| BF_SOFT
| BF_MIDDLE
| BF_ADJUST
);
1289 DrawEdge(hdc
, &right_half
, EDGE_RAISED
,
1290 BF_DIAGONAL_ENDBOTTOMLEFT
| BF_SOFT
| BF_MIDDLE
| BF_ADJUST
);
1292 // If the button is pressed, draw hatching.
1293 if (extra
.classic_state
& DFCS_PUSHED
) {
1295 SetCheckerboardShader(&paint
, rect_win
);
1297 // Fill all three pieces with the pattern.
1298 canvas
->drawIRect(skia::RECTToSkIRect(top_section
), paint
);
1300 SkScalar left_triangle_top
= SkIntToScalar(left_half
.top
);
1301 SkScalar left_triangle_right
= SkIntToScalar(left_half
.right
);
1302 SkPath left_triangle
;
1303 left_triangle
.moveTo(SkIntToScalar(left_half
.left
), left_triangle_top
);
1304 left_triangle
.lineTo(left_triangle_right
, left_triangle_top
);
1305 left_triangle
.lineTo(left_triangle_right
,
1306 SkIntToScalar(left_half
.bottom
));
1307 left_triangle
.close();
1308 canvas
->drawPath(left_triangle
, paint
);
1310 SkScalar right_triangle_left
= SkIntToScalar(right_half
.left
);
1311 SkScalar right_triangle_top
= SkIntToScalar(right_half
.top
);
1312 SkPath right_triangle
;
1313 right_triangle
.moveTo(right_triangle_left
, right_triangle_top
);
1314 right_triangle
.lineTo(SkIntToScalar(right_half
.right
),
1315 right_triangle_top
);
1316 right_triangle
.lineTo(right_triangle_left
,
1317 SkIntToScalar(right_half
.bottom
));
1318 right_triangle
.close();
1319 canvas
->drawPath(right_triangle
, paint
);
1325 HRESULT
NativeThemeWin::PaintProgressBar(
1327 const gfx::Rect
& rect
,
1328 const ProgressBarExtraParams
& extra
) const {
1329 // There is no documentation about the animation speed, frame-rate, nor
1330 // size of moving overlay of the indeterminate progress bar.
1331 // So we just observed real-world programs and guessed following parameters.
1332 const int kDeteminateOverlayPixelsPerSecond
= 300;
1333 const int kDeteminateOverlayWidth
= 120;
1334 const int kIndeterminateOverlayPixelsPerSecond
= 175;
1335 const int kVistaIndeterminateOverlayWidth
= 120;
1336 const int kXPIndeterminateOverlayWidth
= 55;
1337 // The thickness of the bar frame inside |value_rect|
1338 const int kXPBarPadding
= 3;
1340 RECT bar_rect
= rect
.ToRECT();
1341 RECT value_rect
= gfx::Rect(extra
.value_rect_x
,
1343 extra
.value_rect_width
,
1344 extra
.value_rect_height
).ToRECT();
1346 bool pre_vista
= base::win::GetVersion() < base::win::VERSION_VISTA
;
1347 HANDLE handle
= GetThemeHandle(PROGRESS
);
1348 if (handle
&& draw_theme_
&& draw_theme_ex_
) {
1349 draw_theme_(handle
, hdc
, PP_BAR
, 0, &bar_rect
, NULL
);
1351 int bar_width
= bar_rect
.right
- bar_rect
.left
;
1352 if (extra
.determinate
) {
1353 // TODO(morrita): this RTL guess can be wrong.
1354 // We should pass the direction from WebKit side.
1355 bool is_rtl
= (bar_rect
.right
== value_rect
.right
&&
1356 bar_rect
.left
!= value_rect
.left
);
1357 // We should care the direction here because PP_CNUNK painting
1359 DTBGOPTS value_draw_options
;
1360 value_draw_options
.dwSize
= sizeof(DTBGOPTS
);
1361 value_draw_options
.dwFlags
= is_rtl
? DTBG_MIRRORDC
: 0;
1362 value_draw_options
.rcClip
= bar_rect
;
1365 // On XP, progress bar is chunk-style and has no glossy effect.
1366 // We need to shrink destination rect to fit the part inside the bar
1367 // with an appropriate margin.
1368 RECT shrunk_value_rect
= InsetRect(&value_rect
, kXPBarPadding
);
1369 draw_theme_ex_(handle
, hdc
, PP_CHUNK
, 0,
1370 &shrunk_value_rect
, &value_draw_options
);
1372 // On Vista or later, the progress bar part has a
1373 // single-block value part. It also has glossy effect.
1374 // And the value part has exactly same height as the bar part
1375 // so we don't need to shrink the rect.
1376 draw_theme_ex_(handle
, hdc
, PP_FILL
, 0,
1377 &value_rect
, &value_draw_options
);
1379 int dx
= ComputeAnimationProgress(bar_width
,
1380 kDeteminateOverlayWidth
,
1381 kDeteminateOverlayPixelsPerSecond
,
1382 extra
.animated_seconds
);
1383 RECT overlay_rect
= value_rect
;
1384 overlay_rect
.left
+= dx
;
1385 overlay_rect
.right
= overlay_rect
.left
+ kDeteminateOverlayWidth
;
1386 draw_theme_(handle
, hdc
, PP_MOVEOVERLAY
, 0, &overlay_rect
, &value_rect
);
1389 // A glossy overlay for indeterminate progress bar has small pause
1390 // after each animation. We emulate this by adding an invisible margin
1391 // the animation has to traverse.
1392 int width_with_margin
= bar_width
+ kIndeterminateOverlayPixelsPerSecond
;
1393 int overlay_width
= pre_vista
?
1394 kXPIndeterminateOverlayWidth
: kVistaIndeterminateOverlayWidth
;
1395 int dx
= ComputeAnimationProgress(width_with_margin
,
1397 kIndeterminateOverlayPixelsPerSecond
,
1398 extra
.animated_seconds
);
1399 RECT overlay_rect
= bar_rect
;
1400 overlay_rect
.left
+= dx
;
1401 overlay_rect
.right
= overlay_rect
.left
+ overlay_width
;
1403 RECT shrunk_rect
= InsetRect(&overlay_rect
, kXPBarPadding
);
1404 RECT shrunk_bar_rect
= InsetRect(&bar_rect
, kXPBarPadding
);
1405 draw_theme_(handle
, hdc
, PP_CHUNK
, 0, &shrunk_rect
, &shrunk_bar_rect
);
1407 draw_theme_(handle
, hdc
, PP_MOVEOVERLAY
, 0, &overlay_rect
, &bar_rect
);
1414 HBRUSH bg_brush
= GetSysColorBrush(COLOR_BTNFACE
);
1415 HBRUSH fg_brush
= GetSysColorBrush(COLOR_BTNSHADOW
);
1416 FillRect(hdc
, &bar_rect
, bg_brush
);
1417 FillRect(hdc
, &value_rect
, fg_brush
);
1418 DrawEdge(hdc
, &bar_rect
, EDGE_SUNKEN
, BF_RECT
| BF_ADJUST
);
1422 HRESULT
NativeThemeWin::PaintWindowResizeGripper(HDC hdc
,
1423 const gfx::Rect
& rect
) const {
1424 HANDLE handle
= GetThemeHandle(STATUS
);
1425 RECT rect_win
= rect
.ToRECT();
1426 if (handle
&& draw_theme_
) {
1427 // Paint the status bar gripper. There doesn't seem to be a
1428 // standard gripper in Windows for the space between
1429 // scrollbars. This is pretty close, but it's supposed to be
1430 // painted over a status bar.
1431 return draw_theme_(handle
, hdc
, SP_GRIPPER
, 0, &rect_win
, NULL
);
1434 // Draw a windows classic scrollbar gripper.
1435 DrawFrameControl(hdc
, &rect_win
, DFC_SCROLL
, DFCS_SCROLLSIZEGRIP
);
1439 HRESULT
NativeThemeWin::PaintTabPanelBackground(HDC hdc
,
1440 const gfx::Rect
& rect
) const {
1441 HANDLE handle
= GetThemeHandle(TAB
);
1442 RECT rect_win
= rect
.ToRECT();
1443 if (handle
&& draw_theme_
)
1444 return draw_theme_(handle
, hdc
, TABP_BODY
, 0, &rect_win
, NULL
);
1446 // Classic just renders a flat color background.
1447 FillRect(hdc
, &rect_win
, reinterpret_cast<HBRUSH
>(COLOR_3DFACE
+ 1));
1451 HRESULT
NativeThemeWin::PaintTextField(
1455 const gfx::Rect
& rect
,
1456 const TextFieldExtraParams
& extra
) const {
1457 int part_id
= EP_EDITTEXT
;
1458 int state_id
= ETS_NORMAL
;
1461 if (extra
.is_read_only
) {
1462 state_id
= ETS_READONLY
;
1463 } else if (extra
.is_focused
) {
1464 state_id
= ETS_FOCUSED
;
1466 state_id
= ETS_NORMAL
;
1473 state_id
= ETS_SELECTED
;
1476 state_id
= ETS_DISABLED
;
1479 NOTREACHED() << "Invalid state: " << state
;
1483 RECT rect_win
= rect
.ToRECT();
1484 return PaintTextField(hdc
, part_id
, state_id
, extra
.classic_state
,
1486 skia::SkColorToCOLORREF(extra
.background_color
),
1487 extra
.fill_content_area
, extra
.draw_edges
);
1490 HRESULT
NativeThemeWin::PaintTextField(HDC hdc
,
1496 bool fill_content_area
,
1497 bool draw_edges
) const {
1498 // TODO(ojan): http://b/1210017 Figure out how to give the ability to
1499 // exclude individual edges from being drawn.
1501 HANDLE handle
= GetThemeHandle(TEXTFIELD
);
1502 // TODO(mpcomplete): can we detect if the color is specified by the user,
1503 // and if not, just use the system color?
1504 // CreateSolidBrush() accepts a RGB value but alpha must be 0.
1505 HBRUSH bg_brush
= CreateSolidBrush(color
);
1507 // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible
1508 // draw_theme_ex_ is NULL and draw_theme_ is non-null.
1509 if (handle
&& (draw_theme_ex_
|| (draw_theme_
&& draw_edges
))) {
1510 if (draw_theme_ex_
) {
1511 static const DTBGOPTS omit_border_options
= {
1516 const DTBGOPTS
* draw_opts
= draw_edges
? NULL
: &omit_border_options
;
1517 hr
= draw_theme_ex_(handle
, hdc
, part_id
, state_id
, rect
, draw_opts
);
1519 hr
= draw_theme_(handle
, hdc
, part_id
, state_id
, rect
, NULL
);
1522 // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
1523 if (fill_content_area
&& get_theme_content_rect_
) {
1525 hr
= get_theme_content_rect_(handle
, hdc
, part_id
, state_id
, rect
,
1527 FillRect(hdc
, &content_rect
, bg_brush
);
1530 // Draw it manually.
1532 DrawEdge(hdc
, rect
, EDGE_SUNKEN
, BF_RECT
| BF_ADJUST
);
1534 if (fill_content_area
) {
1535 FillRect(hdc
, rect
, (classic_state
& DFCS_INACTIVE
) ?
1536 reinterpret_cast<HBRUSH
>(COLOR_BTNFACE
+ 1) : bg_brush
);
1540 DeleteObject(bg_brush
);
1545 NativeThemeWin::ThemeName
NativeThemeWin::GetThemeName(Part part
) {
1553 case kInnerSpinButton
:
1557 case kMenuPopupGutter
:
1559 case kMenuPopupArrow
:
1560 case kMenuPopupSeparator
:
1566 case kScrollbarDownArrow
:
1567 case kScrollbarLeftArrow
:
1568 case kScrollbarRightArrow
:
1569 case kScrollbarUpArrow
:
1570 case kScrollbarHorizontalThumb
:
1571 case kScrollbarVerticalThumb
:
1572 case kScrollbarHorizontalTrack
:
1573 case kScrollbarVerticalTrack
:
1583 case kWindowResizeGripper
:
1587 NOTREACHED() << "Invalid part: " << part
;
1594 int NativeThemeWin::GetWindowsPart(Part part
,
1596 const ExtraParams
& extra
) {
1600 part_id
= BP_CHECKBOX
;
1603 part_id
= MENU_POPUPCHECK
;
1605 case kMenuPopupArrow
:
1606 part_id
= MENU_POPUPSUBMENU
;
1608 case kMenuPopupGutter
:
1609 part_id
= MENU_POPUPGUTTER
;
1611 case kMenuPopupSeparator
:
1612 part_id
= MENU_POPUPSEPARATOR
;
1615 part_id
= BP_PUSHBUTTON
;
1618 part_id
= BP_RADIOBUTTON
;
1620 case kWindowResizeGripper
:
1621 part_id
= SP_GRIPPER
;
1623 case kScrollbarDownArrow
:
1624 case kScrollbarLeftArrow
:
1625 case kScrollbarRightArrow
:
1626 case kScrollbarUpArrow
:
1627 part_id
= SBP_ARROWBTN
;
1629 case kScrollbarHorizontalThumb
:
1630 part_id
= extra
.scrollbar_track
.is_upper
? SBP_UPPERTRACKHORZ
:
1633 case kScrollbarVerticalThumb
:
1634 part_id
= extra
.scrollbar_track
.is_upper
? SBP_UPPERTRACKVERT
:
1638 NOTREACHED() << "Invalid part: " << part
;
1644 int NativeThemeWin::GetWindowsState(Part part
,
1646 const ExtraParams
& extra
) {
1652 state_id
= CBS_UNCHECKEDNORMAL
;
1655 state_id
= CBS_UNCHECKEDHOT
;
1658 state_id
= CBS_UNCHECKEDPRESSED
;
1661 state_id
= CBS_UNCHECKEDDISABLED
;
1664 NOTREACHED() << "Invalid state: " << state
;
1673 state_id
= extra
.menu_check
.is_radio
? MC_BULLETNORMAL
1674 : MC_CHECKMARKNORMAL
;
1677 state_id
= extra
.menu_check
.is_radio
? MC_BULLETDISABLED
1678 : MC_CHECKMARKDISABLED
;
1681 NOTREACHED() << "Invalid state: " << state
;
1685 case kMenuPopupArrow
:
1686 case kMenuPopupGutter
:
1687 case kMenuPopupSeparator
:
1690 state_id
= MBI_NORMAL
;
1696 state_id
= MBI_PUSHED
;
1699 state_id
= MBI_DISABLED
;
1702 NOTREACHED() << "Invalid state: " << state
;
1709 state_id
= PBS_NORMAL
;
1715 state_id
= PBS_PRESSED
;
1718 state_id
= PBS_DISABLED
;
1721 NOTREACHED() << "Invalid state: " << state
;
1728 state_id
= RBS_UNCHECKEDNORMAL
;
1731 state_id
= RBS_UNCHECKEDHOT
;
1734 state_id
= RBS_UNCHECKEDPRESSED
;
1737 state_id
= RBS_UNCHECKEDDISABLED
;
1740 NOTREACHED() << "Invalid state: " << state
;
1744 case kWindowResizeGripper
:
1750 state_id
= 1; // gripper has no windows state
1753 NOTREACHED() << "Invalid state: " << state
;
1757 case kScrollbarDownArrow
:
1760 state_id
= ABS_DOWNNORMAL
;
1763 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1764 state_id
= base::win::GetVersion() < base::win::VERSION_VISTA
?
1765 ABS_DOWNHOT
: ABS_DOWNHOVER
;
1768 state_id
= ABS_DOWNPRESSED
;
1771 state_id
= ABS_DOWNDISABLED
;
1774 NOTREACHED() << "Invalid state: " << state
;
1778 case kScrollbarLeftArrow
:
1781 state_id
= ABS_LEFTNORMAL
;
1784 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1785 state_id
= base::win::GetVersion() < base::win::VERSION_VISTA
?
1786 ABS_LEFTHOT
: ABS_LEFTHOVER
;
1789 state_id
= ABS_LEFTPRESSED
;
1792 state_id
= ABS_LEFTDISABLED
;
1795 NOTREACHED() << "Invalid state: " << state
;
1799 case kScrollbarRightArrow
:
1802 state_id
= ABS_RIGHTNORMAL
;
1805 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1806 state_id
= base::win::GetVersion() < base::win::VERSION_VISTA
?
1807 ABS_RIGHTHOT
: ABS_RIGHTHOVER
;
1810 state_id
= ABS_RIGHTPRESSED
;
1813 state_id
= ABS_RIGHTDISABLED
;
1816 NOTREACHED() << "Invalid state: " << state
;
1820 case kScrollbarUpArrow
:
1823 state_id
= ABS_UPNORMAL
;
1826 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1827 state_id
= base::win::GetVersion() < base::win::VERSION_VISTA
?
1828 ABS_UPHOT
: ABS_UPHOVER
;
1831 state_id
= ABS_UPPRESSED
;
1834 state_id
= ABS_UPDISABLED
;
1837 NOTREACHED() << "Invalid state: " << state
;
1841 case kScrollbarHorizontalThumb
:
1842 case kScrollbarVerticalThumb
:
1845 state_id
= SCRBS_NORMAL
;
1848 // Mimic WebKit's behaviour in ScrollbarThemeChromiumWin.cpp.
1849 state_id
= base::win::GetVersion() < base::win::VERSION_VISTA
?
1850 SCRBS_HOT
: SCRBS_HOVER
;
1853 state_id
= SCRBS_PRESSED
;
1856 state_id
= SCRBS_DISABLED
;
1859 NOTREACHED() << "Invalid state: " << state
;
1864 NOTREACHED() << "Invalid part: " << part
;
1870 HRESULT
NativeThemeWin::GetThemeInt(ThemeName theme
,
1875 HANDLE handle
= GetThemeHandle(theme
);
1876 if (handle
&& get_theme_int_
)
1877 return get_theme_int_(handle
, part_id
, state_id
, prop_id
, value
);
1881 HRESULT
NativeThemeWin::PaintFrameControl(HDC hdc
,
1882 const gfx::Rect
& rect
,
1886 State control_state
) const {
1887 const int width
= rect
.width();
1888 const int height
= rect
.height();
1890 // DrawFrameControl for menu arrow/check wants a monochrome bitmap.
1891 base::win::ScopedBitmap
mask_bitmap(CreateBitmap(width
, height
, 1, 1, NULL
));
1893 if (mask_bitmap
== NULL
)
1894 return E_OUTOFMEMORY
;
1896 base::win::ScopedCreateDC
bitmap_dc(CreateCompatibleDC(NULL
));
1897 base::win::ScopedSelectObject
select_bitmap(bitmap_dc
, mask_bitmap
);
1898 RECT local_rect
= { 0, 0, width
, height
};
1899 DrawFrameControl(bitmap_dc
, &local_rect
, type
, state
);
1901 // We're going to use BitBlt with a b&w mask. This results in using the dest
1902 // dc's text color for the black bits in the mask, and the dest dc's
1903 // background color for the white bits in the mask. DrawFrameControl draws the
1904 // check in black, and the background in white.
1907 switch (control_state
) {
1908 case NativeTheme::kHovered
:
1909 bg_color_key
= COLOR_HIGHLIGHT
;
1910 text_color_key
= COLOR_HIGHLIGHTTEXT
;
1912 case NativeTheme::kNormal
:
1913 bg_color_key
= COLOR_MENU
;
1914 text_color_key
= COLOR_MENUTEXT
;
1916 case NativeTheme::kDisabled
:
1917 bg_color_key
= is_selected
? COLOR_HIGHLIGHT
: COLOR_MENU
;
1918 text_color_key
= COLOR_GRAYTEXT
;
1922 bg_color_key
= COLOR_MENU
;
1923 text_color_key
= COLOR_MENUTEXT
;
1926 COLORREF old_bg_color
= SetBkColor(hdc
, GetSysColor(bg_color_key
));
1927 COLORREF old_text_color
= SetTextColor(hdc
, GetSysColor(text_color_key
));
1928 BitBlt(hdc
, rect
.x(), rect
.y(), width
, height
, bitmap_dc
, 0, 0, SRCCOPY
);
1929 SetBkColor(hdc
, old_bg_color
);
1930 SetTextColor(hdc
, old_text_color
);
1935 HANDLE
NativeThemeWin::GetThemeHandle(ThemeName theme_name
) const {
1936 if (!open_theme_
|| theme_name
< 0 || theme_name
>= LAST
)
1939 if (theme_handles_
[theme_name
])
1940 return theme_handles_
[theme_name
];
1942 // Not found, try to load it.
1944 switch (theme_name
) {
1946 handle
= open_theme_(NULL
, L
"Button");
1949 handle
= open_theme_(NULL
, L
"Listview");
1952 handle
= open_theme_(NULL
, L
"Menu");
1955 handle
= open_theme_(NULL
, L
"Combobox");
1958 handle
= open_theme_(NULL
, L
"Scrollbar");
1961 handle
= open_theme_(NULL
, L
"Status");
1964 handle
= open_theme_(NULL
, L
"Tab");
1967 handle
= open_theme_(NULL
, L
"Edit");
1970 handle
= open_theme_(NULL
, L
"Trackbar");
1973 handle
= open_theme_(NULL
, L
"Window");
1976 handle
= open_theme_(NULL
, L
"Progress");
1979 handle
= open_theme_(NULL
, L
"Spin");
1984 theme_handles_
[theme_name
] = handle
;