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 if (is_theme_active_
)
136 return !!is_theme_active_();
140 bool NativeThemeWin::IsUsingHighContrastTheme() const {
141 if (is_using_high_contrast_valid_
)
142 return is_using_high_contrast_
;
144 result
.cbSize
= sizeof(HIGHCONTRAST
);
145 is_using_high_contrast_
=
146 SystemParametersInfo(SPI_GETHIGHCONTRAST
, result
.cbSize
, &result
, 0) &&
147 (result
.dwFlags
& HCF_HIGHCONTRASTON
) == HCF_HIGHCONTRASTON
;
148 is_using_high_contrast_valid_
= true;
149 return is_using_high_contrast_
;
152 HRESULT
NativeThemeWin::GetThemeColor(ThemeName theme
,
156 SkColor
* color
) const {
157 HANDLE handle
= GetThemeHandle(theme
);
158 if (handle
&& get_theme_color_
) {
160 if (get_theme_color_(handle
, part_id
, state_id
, prop_id
, &color_ref
) ==
162 *color
= skia::COLORREFToSkColor(color_ref
);
169 SkColor
NativeThemeWin::GetThemeColorWithDefault(ThemeName theme
,
173 int default_sys_color
) const {
175 if (GetThemeColor(theme
, part_id
, state_id
, prop_id
, &color
) != S_OK
)
176 color
= color_utils::GetSysSkColor(default_sys_color
);
180 gfx::Size
NativeThemeWin::GetThemeBorderSize(ThemeName theme
) const {
181 // For simplicity use the wildcard state==0, part==0, since it works
182 // for the cases we currently depend on.
184 if (GetThemeInt(theme
, 0, 0, TMT_BORDERSIZE
, &border
) == S_OK
)
185 return gfx::Size(border
, border
);
187 return gfx::Size(GetSystemMetrics(SM_CXEDGE
), GetSystemMetrics(SM_CYEDGE
));
190 void NativeThemeWin::DisableTheming() const {
191 if (!set_theme_properties_
)
193 set_theme_properties_(0);
196 void NativeThemeWin::CloseHandles() const {
200 for (int i
= 0; i
< LAST
; ++i
) {
201 if (theme_handles_
[i
]) {
202 close_theme_(theme_handles_
[i
]);
203 theme_handles_
[i
] = NULL
;
208 bool NativeThemeWin::IsClassicTheme(ThemeName name
) const {
212 return !GetThemeHandle(name
);
216 NativeThemeWin
* NativeThemeWin::instance() {
217 CR_DEFINE_STATIC_LOCAL(NativeThemeWin
, s_native_theme
, ());
218 return &s_native_theme
;
221 gfx::Size
NativeThemeWin::GetPartSize(Part part
,
223 const ExtraParams
& extra
) const {
224 gfx::Size part_size
= CommonThemeGetPartSize(part
, state
, extra
);
225 if (!part_size
.IsEmpty())
228 // The GetThemePartSize call below returns the default size without
229 // accounting for user customization (crbug/218291).
231 case kScrollbarDownArrow
:
232 case kScrollbarLeftArrow
:
233 case kScrollbarRightArrow
:
234 case kScrollbarUpArrow
:
235 case kScrollbarHorizontalThumb
:
236 case kScrollbarVerticalThumb
:
237 case kScrollbarHorizontalTrack
:
238 case kScrollbarVerticalTrack
: {
239 int size
= gfx::win::GetSystemMetricsInDIP(SM_CXVSCROLL
);
242 return gfx::Size(size
, size
);
246 int part_id
= GetWindowsPart(part
, state
, extra
);
247 int state_id
= GetWindowsState(part
, state
, extra
);
250 HDC hdc
= GetDC(NULL
);
251 HRESULT hr
= GetThemePartSize(GetThemeName(part
), hdc
, part_id
, state_id
,
252 NULL
, TS_TRUE
, &size
);
253 ReleaseDC(NULL
, hdc
);
256 // TODO(rogerta): For now, we need to support radio buttons and checkboxes
257 // when theming is not enabled. Support for other parts can be added
262 // TODO(rogerta): I was not able to find any API to get the default
263 // size of these controls, so determined these values empirically.
274 return gfx::Size(size
.cx
, size
.cy
);
277 void NativeThemeWin::Paint(SkCanvas
* canvas
,
280 const gfx::Rect
& rect
,
281 const ExtraParams
& extra
) const {
287 CommonThemePaintComboboxArrow(canvas
, rect
);
289 case kMenuPopupGutter
:
290 CommonThemePaintMenuGutter(canvas
, rect
);
292 case kMenuPopupSeparator
:
293 CommonThemePaintMenuSeparator(canvas
, rect
, extra
.menu_separator
);
295 case kMenuPopupBackground
:
296 CommonThemePaintMenuBackground(canvas
, rect
);
298 case kMenuItemBackground
:
299 CommonThemePaintMenuItemBackground(canvas
, state
, rect
);
303 bool needs_paint_indirect
= false;
304 if (!skia::SupportsPlatformPaint(canvas
)) {
305 // This block will only get hit with --enable-accelerated-drawing flag.
306 needs_paint_indirect
= true;
308 // Scrollbar components on Windows Classic theme (on all Windows versions)
309 // have particularly problematic alpha values, so always draw them
310 // indirectly. In addition, scrollbar thumbs and grippers for the Windows XP
311 // theme (available only on Windows XP) also need their alpha values
314 case kScrollbarDownArrow
:
315 case kScrollbarUpArrow
:
316 case kScrollbarLeftArrow
:
317 case kScrollbarRightArrow
:
318 if (!GetThemeHandle(SCROLLBAR
))
319 needs_paint_indirect
= true;
321 case kScrollbarHorizontalThumb
:
322 case kScrollbarVerticalThumb
:
323 case kScrollbarHorizontalGripper
:
324 case kScrollbarVerticalGripper
:
325 if (!GetThemeHandle(SCROLLBAR
) ||
326 base::win::GetVersion() == base::win::VERSION_XP
)
327 needs_paint_indirect
= true;
334 if (needs_paint_indirect
)
335 PaintIndirect(canvas
, part
, state
, rect
, extra
);
337 PaintDirect(canvas
, part
, state
, rect
, extra
);
340 NativeThemeWin::NativeThemeWin()
341 : theme_dll_(LoadLibrary(L
"uxtheme.dll")),
343 draw_theme_ex_(NULL
),
344 get_theme_color_(NULL
),
345 get_theme_content_rect_(NULL
),
346 get_theme_part_size_(NULL
),
349 set_theme_properties_(NULL
),
350 is_theme_active_(NULL
),
351 get_theme_int_(NULL
),
352 color_change_listener_(this),
353 is_using_high_contrast_(false),
354 is_using_high_contrast_valid_(false) {
356 draw_theme_
= reinterpret_cast<DrawThemeBackgroundPtr
>(
357 GetProcAddress(theme_dll_
, "DrawThemeBackground"));
358 draw_theme_ex_
= reinterpret_cast<DrawThemeBackgroundExPtr
>(
359 GetProcAddress(theme_dll_
, "DrawThemeBackgroundEx"));
360 get_theme_color_
= reinterpret_cast<GetThemeColorPtr
>(
361 GetProcAddress(theme_dll_
, "GetThemeColor"));
362 get_theme_content_rect_
= reinterpret_cast<GetThemeContentRectPtr
>(
363 GetProcAddress(theme_dll_
, "GetThemeBackgroundContentRect"));
364 get_theme_part_size_
= reinterpret_cast<GetThemePartSizePtr
>(
365 GetProcAddress(theme_dll_
, "GetThemePartSize"));
366 open_theme_
= reinterpret_cast<OpenThemeDataPtr
>(
367 GetProcAddress(theme_dll_
, "OpenThemeData"));
368 close_theme_
= reinterpret_cast<CloseThemeDataPtr
>(
369 GetProcAddress(theme_dll_
, "CloseThemeData"));
370 set_theme_properties_
= reinterpret_cast<SetThemeAppPropertiesPtr
>(
371 GetProcAddress(theme_dll_
, "SetThemeAppProperties"));
372 is_theme_active_
= reinterpret_cast<IsThemeActivePtr
>(
373 GetProcAddress(theme_dll_
, "IsThemeActive"));
374 get_theme_int_
= reinterpret_cast<GetThemeIntPtr
>(
375 GetProcAddress(theme_dll_
, "GetThemeInt"));
377 memset(theme_handles_
, 0, sizeof(theme_handles_
));
379 // Initialize the cached system colors.
380 UpdateSystemColors();
383 NativeThemeWin::~NativeThemeWin() {
385 // todo (cpu): fix this soon. Making a call to CloseHandles() here breaks
386 // certain tests and the reliability bots.
388 FreeLibrary(theme_dll_
);
392 void NativeThemeWin::OnSysColorChange() {
393 UpdateSystemColors();
394 is_using_high_contrast_valid_
= false;
398 void NativeThemeWin::UpdateSystemColors() {
399 for (int i
= 0; i
< arraysize(kSystemColors
); ++i
) {
400 system_colors_
[kSystemColors
[i
]] =
401 color_utils::GetSysSkColor(kSystemColors
[i
]);
405 void NativeThemeWin::PaintDirect(SkCanvas
* canvas
,
408 const gfx::Rect
& rect
,
409 const ExtraParams
& extra
) const {
410 skia::ScopedPlatformPaint
scoped_platform_paint(canvas
);
411 HDC hdc
= scoped_platform_paint
.GetPlatformSurface();
415 PaintCheckbox(hdc
, part
, state
, rect
, extra
.button
);
418 PaintRadioButton(hdc
, part
, state
, rect
, extra
.button
);
421 PaintPushButton(hdc
, part
, state
, rect
, extra
.button
);
423 case kMenuPopupArrow
:
424 PaintMenuArrow(hdc
, state
, rect
, extra
.menu_arrow
);
426 case kMenuPopupGutter
:
427 PaintMenuGutter(hdc
, rect
);
429 case kMenuPopupSeparator
:
430 PaintMenuSeparator(hdc
, rect
, extra
.menu_separator
);
432 case kMenuPopupBackground
:
433 PaintMenuBackground(hdc
, rect
);
436 PaintMenuCheck(hdc
, state
, rect
, extra
.menu_check
);
438 case kMenuCheckBackground
:
439 PaintMenuCheckBackground(hdc
, state
, rect
);
441 case kMenuItemBackground
:
442 PaintMenuItemBackground(hdc
, state
, rect
, extra
.menu_item
);
445 PaintMenuList(hdc
, state
, rect
, extra
.menu_list
);
447 case kScrollbarDownArrow
:
448 case kScrollbarUpArrow
:
449 case kScrollbarLeftArrow
:
450 case kScrollbarRightArrow
:
451 PaintScrollbarArrow(hdc
, part
, state
, rect
, extra
.scrollbar_arrow
);
453 case kScrollbarHorizontalTrack
:
454 case kScrollbarVerticalTrack
:
455 PaintScrollbarTrack(canvas
, hdc
, part
, state
, rect
,
456 extra
.scrollbar_track
);
458 case kScrollbarCorner
:
459 canvas
->drawColor(SK_ColorWHITE
, SkXfermode::kSrc_Mode
);
461 case kScrollbarHorizontalThumb
:
462 case kScrollbarVerticalThumb
:
463 case kScrollbarHorizontalGripper
:
464 case kScrollbarVerticalGripper
:
465 PaintScrollbarThumb(hdc
, part
, state
, rect
, extra
.scrollbar_thumb
);
467 case kInnerSpinButton
:
468 PaintSpinButton(hdc
, part
, state
, rect
, extra
.inner_spin
);
472 PaintTrackbar(canvas
, hdc
, part
, state
, rect
, extra
.trackbar
);
475 PaintProgressBar(hdc
, rect
, extra
.progress_bar
);
477 case kWindowResizeGripper
:
478 PaintWindowResizeGripper(hdc
, rect
);
480 case kTabPanelBackground
:
481 PaintTabPanelBackground(hdc
, rect
);
484 PaintTextField(hdc
, part
, state
, rect
, extra
.text_field
);
490 // While transitioning NativeThemeWin to the single Paint() entry point,
491 // unsupported parts will DCHECK here.
496 SkColor
NativeThemeWin::GetSystemColor(ColorId color_id
) const {
498 if (CommonThemeGetSystemColor(color_id
, &color
))
503 case kColorId_WindowBackground
:
504 return system_colors_
[COLOR_WINDOW
];
507 case kColorId_DialogBackground
:
508 if (gfx::IsInvertedColorScheme())
509 return color_utils::InvertColor(kDialogBackgroundColor
);
510 return kDialogBackgroundColor
;
513 case kColorId_FocusedBorderColor
:
514 return kFocusedBorderColor
;
515 case kColorId_UnfocusedBorderColor
:
516 return kUnfocusedBorderColor
;
519 case kColorId_ButtonBackgroundColor
:
520 return kButtonBackgroundColor
;
521 case kColorId_ButtonEnabledColor
:
522 return system_colors_
[COLOR_BTNTEXT
];
523 case kColorId_ButtonDisabledColor
:
524 return system_colors_
[COLOR_GRAYTEXT
];
525 case kColorId_ButtonHighlightColor
:
526 return kButtonHighlightColor
;
527 case kColorId_ButtonHoverColor
:
528 return kButtonHoverColor
;
529 case kColorId_ButtonHoverBackgroundColor
:
530 return kButtonHoverBackgroundColor
;
533 case kColorId_EnabledMenuItemForegroundColor
:
534 return kEnabledMenuItemForegroundColor
;
535 case kColorId_DisabledMenuItemForegroundColor
:
536 return kDisabledMenuItemForegroundColor
;
537 case kColorId_DisabledEmphasizedMenuItemForegroundColor
:
538 return SK_ColorBLACK
;
539 case kColorId_FocusedMenuItemBackgroundColor
:
540 return kFocusedMenuItemBackgroundColor
;
541 case kColorId_MenuSeparatorColor
:
542 return kMenuSeparatorColor
;
545 case kColorId_LabelEnabledColor
:
546 return system_colors_
[COLOR_BTNTEXT
];
547 case kColorId_LabelDisabledColor
:
548 return system_colors_
[COLOR_GRAYTEXT
];
549 case kColorId_LabelBackgroundColor
:
550 return system_colors_
[COLOR_WINDOW
];
553 case kColorId_TextfieldDefaultColor
:
554 return system_colors_
[COLOR_WINDOWTEXT
];
555 case kColorId_TextfieldDefaultBackground
:
556 return system_colors_
[COLOR_WINDOW
];
557 case kColorId_TextfieldReadOnlyColor
:
558 return system_colors_
[COLOR_GRAYTEXT
];
559 case kColorId_TextfieldReadOnlyBackground
:
560 return system_colors_
[COLOR_3DFACE
];
561 case kColorId_TextfieldSelectionColor
:
562 return system_colors_
[COLOR_HIGHLIGHTTEXT
];
563 case kColorId_TextfieldSelectionBackgroundFocused
:
564 return system_colors_
[COLOR_HIGHLIGHT
];
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);
649 return kInvalidColorIdColor
;
652 void NativeThemeWin::PaintIndirect(SkCanvas
* canvas
,
655 const gfx::Rect
& rect
,
656 const ExtraParams
& extra
) const {
657 // TODO(asvitkine): This path is pretty inefficient - for each paint operation
658 // it creates a new offscreen bitmap Skia canvas. This can
659 // be sped up by doing it only once per part/state and
660 // keeping a cache of the resulting bitmaps.
662 // Create an offscreen canvas that is backed by an HDC.
663 skia::RefPtr
<skia::BitmapPlatformDevice
> device
= skia::AdoptRef(
664 skia::BitmapPlatformDevice::Create(
665 rect
.width(), rect
.height(), false, NULL
));
669 SkCanvas
offscreen_canvas(device
.get());
670 DCHECK(skia::SupportsPlatformPaint(&offscreen_canvas
));
672 // Some of the Windows theme drawing operations do not write correct alpha
673 // values for fully-opaque pixels; instead the pixels get alpha 0. This is
674 // especially a problem on Windows XP or when using the Classic theme.
676 // To work-around this, mark all pixels with a placeholder value, to detect
677 // which pixels get touched by the paint operation. After paint, set any
678 // pixels that have alpha 0 to opaque and placeholders to fully-transparent.
679 const SkColor placeholder
= SkColorSetARGB(1, 0, 0, 0);
680 offscreen_canvas
.clear(placeholder
);
682 // Offset destination rects to have origin (0,0).
683 gfx::Rect
adjusted_rect(rect
.size());
684 ExtraParams
adjusted_extra(extra
);
687 adjusted_extra
.progress_bar
.value_rect_x
= 0;
688 adjusted_extra
.progress_bar
.value_rect_y
= 0;
690 case kScrollbarHorizontalTrack
:
691 case kScrollbarVerticalTrack
:
692 adjusted_extra
.scrollbar_track
.track_x
= 0;
693 adjusted_extra
.scrollbar_track
.track_y
= 0;
697 // Draw the theme controls using existing HDC-drawing code.
698 PaintDirect(&offscreen_canvas
,
704 // Copy the pixels to a bitmap that has ref-counted pixel storage, which is
705 // necessary to have when drawing to a SkPicture.
706 const SkBitmap
& hdc_bitmap
=
707 offscreen_canvas
.getDevice()->accessBitmap(false);
709 hdc_bitmap
.copyTo(&bitmap
, kN32_SkColorType
);
711 // Post-process the pixels to fix up the alpha values (see big comment above).
712 const SkPMColor placeholder_value
= SkPreMultiplyColor(placeholder
);
713 const int pixel_count
= rect
.width() * rect
.height();
714 SkPMColor
* pixels
= bitmap
.getAddr32(0, 0);
715 for (int i
= 0; i
< pixel_count
; i
++) {
716 if (pixels
[i
] == placeholder_value
) {
717 // Pixel wasn't touched - make it fully transparent.
718 pixels
[i
] = SkPackARGB32(0, 0, 0, 0);
719 } else if (SkGetPackedA32(pixels
[i
]) == 0) {
720 // Pixel was touched but has incorrect alpha of 0, make it fully opaque.
721 pixels
[i
] = SkPackARGB32(0xFF,
722 SkGetPackedR32(pixels
[i
]),
723 SkGetPackedG32(pixels
[i
]),
724 SkGetPackedB32(pixels
[i
]));
728 // Draw the offscreen bitmap to the destination canvas.
729 canvas
->drawBitmap(bitmap
, rect
.x(), rect
.y());
732 HRESULT
NativeThemeWin::GetThemePartSize(ThemeName theme_name
,
739 HANDLE handle
= GetThemeHandle(theme_name
);
740 if (handle
&& get_theme_part_size_
)
741 return get_theme_part_size_(handle
, hdc
, part_id
, state_id
, rect
, ts
, size
);
746 HRESULT
NativeThemeWin::PaintButton(HDC hdc
,
748 const ButtonExtraParams
& extra
,
752 HANDLE handle
= GetThemeHandle(BUTTON
);
753 if (handle
&& draw_theme_
)
754 return draw_theme_(handle
, hdc
, part_id
, state_id
, rect
, NULL
);
756 // Adjust classic_state based on part, state, and extras.
757 int classic_state
= extra
.classic_state
;
760 classic_state
|= DFCS_BUTTONCHECK
;
763 classic_state
|= DFCS_BUTTONRADIO
;
766 classic_state
|= DFCS_BUTTONPUSH
;
769 NOTREACHED() << "Unknown part_id: " << part_id
;
775 classic_state
|= DFCS_INACTIVE
;
778 classic_state
|= DFCS_PUSHED
;
784 NOTREACHED() << "Unknown state: " << state
;
789 classic_state
|= DFCS_CHECKED
;
792 // All pressed states have both low bits set, and no other states do.
793 const bool focused
= ((state_id
& ETS_FOCUSED
) == ETS_FOCUSED
);
794 const bool pressed
= ((state_id
& PBS_PRESSED
) == PBS_PRESSED
);
795 if ((BP_PUSHBUTTON
== part_id
) && (pressed
|| focused
)) {
796 // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the
797 // button itself is shrunk by 1 pixel.
798 HBRUSH brush
= GetSysColorBrush(COLOR_3DDKSHADOW
);
800 FrameRect(hdc
, rect
, brush
);
801 InflateRect(rect
, -1, -1);
804 DrawFrameControl(hdc
, rect
, DFC_BUTTON
, classic_state
);
806 // Draw the focus rectangle (the dotted line box) only on buttons. For radio
807 // and checkboxes, we let webkit draw the focus rectangle (orange glow).
808 if ((BP_PUSHBUTTON
== part_id
) && focused
) {
809 // The focus rect is inside the button. The exact number of pixels depends
810 // on whether we're in classic mode or using uxtheme.
811 if (handle
&& get_theme_content_rect_
) {
812 get_theme_content_rect_(handle
, hdc
, part_id
, state_id
, rect
, rect
);
814 InflateRect(rect
, -GetSystemMetrics(SM_CXEDGE
),
815 -GetSystemMetrics(SM_CYEDGE
));
817 DrawFocusRect(hdc
, rect
);
820 // Classic theme doesn't support indeterminate checkboxes. We draw
821 // a recangle inside a checkbox like IE10 does.
822 if (part_id
== BP_CHECKBOX
&& extra
.indeterminate
) {
823 RECT inner_rect
= *rect
;
824 // "4 / 13" is same as IE10 in classic theme.
825 int padding
= (inner_rect
.right
- inner_rect
.left
) * 4 / 13;
826 InflateRect(&inner_rect
, -padding
, -padding
);
827 int color_index
= state
== kDisabled
? COLOR_GRAYTEXT
: COLOR_WINDOWTEXT
;
828 FillRect(hdc
, &inner_rect
, GetSysColorBrush(color_index
));
834 HRESULT
NativeThemeWin::PaintMenuSeparator(
836 const gfx::Rect
& rect
,
837 const MenuSeparatorExtraParams
& extra
) const {
838 RECT rect_win
= rect
.ToRECT();
840 HANDLE handle
= GetThemeHandle(MENU
);
841 if (handle
&& draw_theme_
) {
842 // Delta is needed for non-classic to move separator up slightly.
845 return draw_theme_(handle
, hdc
, MENU_POPUPSEPARATOR
, MPI_NORMAL
, &rect_win
,
849 DrawEdge(hdc
, &rect_win
, EDGE_ETCHED
, BF_TOP
);
853 HRESULT
NativeThemeWin::PaintMenuGutter(HDC hdc
,
854 const gfx::Rect
& rect
) const {
855 RECT rect_win
= rect
.ToRECT();
856 HANDLE handle
= GetThemeHandle(MENU
);
857 if (handle
&& draw_theme_
)
858 return draw_theme_(handle
, hdc
, MENU_POPUPGUTTER
, MPI_NORMAL
, &rect_win
,
863 HRESULT
NativeThemeWin::PaintMenuArrow(HDC hdc
,
865 const gfx::Rect
& rect
,
866 const MenuArrowExtraParams
& extra
)
868 int state_id
= MSM_NORMAL
;
869 if (state
== kDisabled
)
870 state_id
= MSM_DISABLED
;
872 HANDLE handle
= GetThemeHandle(MENU
);
873 RECT rect_win
= rect
.ToRECT();
874 if (handle
&& draw_theme_
) {
875 if (extra
.pointing_right
) {
876 return draw_theme_(handle
, hdc
, MENU_POPUPSUBMENU
, state_id
, &rect_win
,
879 // There is no way to tell the uxtheme API to draw a left pointing arrow;
880 // it doesn't have a flag equivalent to DFCS_MENUARROWRIGHT. But they
881 // are needed for RTL locales on Vista. So use a memory DC and mirror
882 // the region with GDI's StretchBlt.
884 base::win::ScopedCreateDC
mem_dc(CreateCompatibleDC(hdc
));
885 base::win::ScopedBitmap
mem_bitmap(CreateCompatibleBitmap(hdc
, r
.width(),
887 base::win::ScopedSelectObject
select_bitmap(mem_dc
, mem_bitmap
);
888 // Copy and horizontally mirror the background from hdc into mem_dc. Use
889 // a negative-width source rect, starting at the rightmost pixel.
890 StretchBlt(mem_dc
, 0, 0, r
.width(), r
.height(),
891 hdc
, r
.right()-1, r
.y(), -r
.width(), r
.height(), SRCCOPY
);
893 RECT theme_rect
= {0, 0, r
.width(), r
.height()};
894 HRESULT result
= draw_theme_(handle
, mem_dc
, MENU_POPUPSUBMENU
,
895 state_id
, &theme_rect
, NULL
);
896 // Copy and mirror the result back into mem_dc.
897 StretchBlt(hdc
, r
.x(), r
.y(), r
.width(), r
.height(),
898 mem_dc
, r
.width()-1, 0, -r
.width(), r
.height(), SRCCOPY
);
903 // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
904 // left pointing arrow. This makes the following 'if' statement slightly
907 if (extra
.pointing_right
)
908 pfc_state
= DFCS_MENUARROW
;
910 pfc_state
= DFCS_MENUARROWRIGHT
;
911 return PaintFrameControl(hdc
, rect
, DFC_MENU
, pfc_state
, extra
.is_selected
,
915 HRESULT
NativeThemeWin::PaintMenuBackground(HDC hdc
,
916 const gfx::Rect
& rect
) const {
917 HANDLE handle
= GetThemeHandle(MENU
);
918 RECT rect_win
= rect
.ToRECT();
919 if (handle
&& draw_theme_
) {
920 HRESULT result
= draw_theme_(handle
, hdc
, MENU_POPUPBACKGROUND
, 0,
922 FrameRect(hdc
, &rect_win
, GetSysColorBrush(COLOR_3DSHADOW
));
926 FillRect(hdc
, &rect_win
, GetSysColorBrush(COLOR_MENU
));
927 DrawEdge(hdc
, &rect_win
, EDGE_RAISED
, BF_RECT
);
931 HRESULT
NativeThemeWin::PaintMenuCheck(
934 const gfx::Rect
& rect
,
935 const MenuCheckExtraParams
& extra
) const {
936 HANDLE handle
= GetThemeHandle(MENU
);
938 if (extra
.is_radio
) {
939 state_id
= state
== kDisabled
? MC_BULLETDISABLED
: MC_BULLETNORMAL
;
941 state_id
= state
== kDisabled
? MC_CHECKMARKDISABLED
: MC_CHECKMARKNORMAL
;
944 RECT rect_win
= rect
.ToRECT();
945 if (handle
&& draw_theme_
)
946 return draw_theme_(handle
, hdc
, MENU_POPUPCHECK
, state_id
, &rect_win
, NULL
);
948 return PaintFrameControl(hdc
, rect
, DFC_MENU
,
949 extra
.is_radio
? DFCS_MENUBULLET
: DFCS_MENUCHECK
,
950 extra
.is_selected
, state
);
953 HRESULT
NativeThemeWin::PaintMenuCheckBackground(HDC hdc
,
955 const gfx::Rect
& rect
) const {
956 HANDLE handle
= GetThemeHandle(MENU
);
957 int state_id
= state
== kDisabled
? MCB_DISABLED
: MCB_NORMAL
;
958 RECT rect_win
= rect
.ToRECT();
959 if (handle
&& draw_theme_
)
960 return draw_theme_(handle
, hdc
, MENU_POPUPCHECKBACKGROUND
, state_id
,
962 // Nothing to do for background.
966 HRESULT
NativeThemeWin::PaintMenuItemBackground(
969 const gfx::Rect
& rect
,
970 const MenuItemExtraParams
& extra
) const {
971 HANDLE handle
= GetThemeHandle(MENU
);
972 RECT rect_win
= rect
.ToRECT();
976 state_id
= MPI_NORMAL
;
979 state_id
= extra
.is_selected
? MPI_DISABLEDHOT
: MPI_DISABLED
;
985 NOTREACHED() << "Invalid state " << state
;
989 if (handle
&& draw_theme_
)
990 return draw_theme_(handle
, hdc
, MENU_POPUPITEM
, state_id
, &rect_win
, NULL
);
992 if (extra
.is_selected
)
993 FillRect(hdc
, &rect_win
, GetSysColorBrush(COLOR_HIGHLIGHT
));
997 HRESULT
NativeThemeWin::PaintPushButton(HDC hdc
,
1000 const gfx::Rect
& rect
,
1001 const ButtonExtraParams
& extra
) const {
1005 state_id
= PBS_DISABLED
;
1011 state_id
= extra
.is_default
? PBS_DEFAULTED
: PBS_NORMAL
;
1014 state_id
= PBS_PRESSED
;
1017 NOTREACHED() << "Invalid state: " << state
;
1021 RECT rect_win
= rect
.ToRECT();
1022 return PaintButton(hdc
, state
, extra
, BP_PUSHBUTTON
, state_id
, &rect_win
);
1025 HRESULT
NativeThemeWin::PaintRadioButton(HDC hdc
,
1028 const gfx::Rect
& rect
,
1029 const ButtonExtraParams
& extra
) const {
1033 state_id
= extra
.checked
? RBS_CHECKEDDISABLED
: RBS_UNCHECKEDDISABLED
;
1036 state_id
= extra
.checked
? RBS_CHECKEDHOT
: RBS_UNCHECKEDHOT
;
1039 state_id
= extra
.checked
? RBS_CHECKEDNORMAL
: RBS_UNCHECKEDNORMAL
;
1042 state_id
= extra
.checked
? RBS_CHECKEDPRESSED
: RBS_UNCHECKEDPRESSED
;
1045 NOTREACHED() << "Invalid state: " << state
;
1049 RECT rect_win
= rect
.ToRECT();
1050 return PaintButton(hdc
, state
, extra
, BP_RADIOBUTTON
, state_id
, &rect_win
);
1053 HRESULT
NativeThemeWin::PaintCheckbox(HDC hdc
,
1056 const gfx::Rect
& rect
,
1057 const ButtonExtraParams
& extra
) const {
1061 state_id
= extra
.checked
? CBS_CHECKEDDISABLED
:
1062 extra
.indeterminate
? CBS_MIXEDDISABLED
:
1063 CBS_UNCHECKEDDISABLED
;
1066 state_id
= extra
.checked
? CBS_CHECKEDHOT
:
1067 extra
.indeterminate
? CBS_MIXEDHOT
:
1071 state_id
= extra
.checked
? CBS_CHECKEDNORMAL
:
1072 extra
.indeterminate
? CBS_MIXEDNORMAL
:
1073 CBS_UNCHECKEDNORMAL
;
1076 state_id
= extra
.checked
? CBS_CHECKEDPRESSED
:
1077 extra
.indeterminate
? CBS_MIXEDPRESSED
:
1078 CBS_UNCHECKEDPRESSED
;
1081 NOTREACHED() << "Invalid state: " << state
;
1085 RECT rect_win
= rect
.ToRECT();
1086 return PaintButton(hdc
, state
, extra
, BP_CHECKBOX
, state_id
, &rect_win
);
1089 HRESULT
NativeThemeWin::PaintMenuList(HDC hdc
,
1091 const gfx::Rect
& rect
,
1092 const MenuListExtraParams
& extra
) const {
1093 HANDLE handle
= GetThemeHandle(MENULIST
);
1094 RECT rect_win
= rect
.ToRECT();
1098 state_id
= CBXS_NORMAL
;
1101 state_id
= CBXS_DISABLED
;
1104 state_id
= CBXS_HOT
;
1107 state_id
= CBXS_PRESSED
;
1110 NOTREACHED() << "Invalid state " << state
;
1114 if (handle
&& draw_theme_
)
1115 return draw_theme_(handle
, hdc
, CP_DROPDOWNBUTTON
, state_id
, &rect_win
,
1118 // Draw it manually.
1119 DrawFrameControl(hdc
, &rect_win
, DFC_SCROLL
,
1120 DFCS_SCROLLCOMBOBOX
| extra
.classic_state
);
1124 HRESULT
NativeThemeWin::PaintScrollbarArrow(
1128 const gfx::Rect
& rect
,
1129 const ScrollbarArrowExtraParams
& extra
) const {
1130 static const int state_id_matrix
[4][kMaxState
] = {
1131 ABS_DOWNDISABLED
, ABS_DOWNHOT
, ABS_DOWNNORMAL
, ABS_DOWNPRESSED
,
1132 ABS_LEFTDISABLED
, ABS_LEFTHOT
, ABS_LEFTNORMAL
, ABS_LEFTPRESSED
,
1133 ABS_RIGHTDISABLED
, ABS_RIGHTHOT
, ABS_RIGHTNORMAL
, ABS_RIGHTPRESSED
,
1134 ABS_UPDISABLED
, ABS_UPHOT
, ABS_UPNORMAL
, ABS_UPPRESSED
1136 HANDLE handle
= GetThemeHandle(SCROLLBAR
);
1137 RECT rect_win
= rect
.ToRECT();
1138 if (handle
&& draw_theme_
) {
1139 int index
= part
- kScrollbarDownArrow
;
1140 DCHECK(index
>=0 && index
< 4);
1141 int state_id
= state_id_matrix
[index
][state
];
1143 // Hovering means that the cursor is over the scroolbar, but not over the
1144 // specific arrow itself. We don't want to show it "hot" mode, but only
1146 if (state
== kHovered
&& extra
.is_hovering
) {
1148 case kScrollbarDownArrow
:
1149 state_id
= ABS_DOWNHOVER
;
1151 case kScrollbarLeftArrow
:
1152 state_id
= ABS_LEFTHOVER
;
1154 case kScrollbarRightArrow
:
1155 state_id
= ABS_RIGHTHOVER
;
1157 case kScrollbarUpArrow
:
1158 state_id
= ABS_UPHOVER
;
1161 NOTREACHED() << "Invalid part: " << part
;
1165 return PaintScaledTheme(handle
, hdc
, SBP_ARROWBTN
, state_id
, rect
);
1168 int classic_state
= DFCS_SCROLLDOWN
;
1170 case kScrollbarDownArrow
:
1171 classic_state
= DFCS_SCROLLDOWN
;
1173 case kScrollbarLeftArrow
:
1174 classic_state
= DFCS_SCROLLLEFT
;
1176 case kScrollbarRightArrow
:
1177 classic_state
= DFCS_SCROLLRIGHT
;
1179 case kScrollbarUpArrow
:
1180 classic_state
= DFCS_SCROLLUP
;
1183 NOTREACHED() << "Invalid part: " << part
;
1188 classic_state
|= DFCS_INACTIVE
;
1191 classic_state
|= DFCS_HOT
;
1196 classic_state
|= DFCS_PUSHED
;
1199 NOTREACHED() << "Invalid state: " << state
;
1202 DrawFrameControl(hdc
, &rect_win
, DFC_SCROLL
, classic_state
);
1206 HRESULT
NativeThemeWin::PaintScrollbarThumb(
1210 const gfx::Rect
& rect
,
1211 const ScrollbarThumbExtraParams
& extra
) const {
1212 HANDLE handle
= GetThemeHandle(SCROLLBAR
);
1213 RECT rect_win
= rect
.ToRECT();
1218 case NativeTheme::kScrollbarHorizontalThumb
:
1219 part_id
= SBP_THUMBBTNHORZ
;
1221 case NativeTheme::kScrollbarVerticalThumb
:
1222 part_id
= SBP_THUMBBTNVERT
;
1224 case NativeTheme::kScrollbarHorizontalGripper
:
1225 part_id
= SBP_GRIPPERHORZ
;
1227 case NativeTheme::kScrollbarVerticalGripper
:
1228 part_id
= SBP_GRIPPERVERT
;
1231 NOTREACHED() << "Invalid part: " << part
;
1237 state_id
= SCRBS_DISABLED
;
1240 state_id
= extra
.is_hovering
? SCRBS_HOVER
: SCRBS_HOT
;
1243 state_id
= SCRBS_NORMAL
;
1246 state_id
= SCRBS_PRESSED
;
1249 NOTREACHED() << "Invalid state: " << state
;
1253 if (handle
&& draw_theme_
)
1254 return PaintScaledTheme(handle
, hdc
, part_id
, state_id
, rect
);
1256 // Draw it manually.
1257 if ((part_id
== SBP_THUMBBTNHORZ
) || (part_id
== SBP_THUMBBTNVERT
))
1258 DrawEdge(hdc
, &rect_win
, EDGE_RAISED
, BF_RECT
| BF_MIDDLE
);
1259 // Classic mode doesn't have a gripper.
1263 HRESULT
NativeThemeWin::PaintScrollbarTrack(
1268 const gfx::Rect
& rect
,
1269 const ScrollbarTrackExtraParams
& extra
) const {
1270 HANDLE handle
= GetThemeHandle(SCROLLBAR
);
1271 RECT rect_win
= rect
.ToRECT();
1276 case NativeTheme::kScrollbarHorizontalTrack
:
1277 part_id
= extra
.is_upper
? SBP_UPPERTRACKHORZ
: SBP_LOWERTRACKHORZ
;
1279 case NativeTheme::kScrollbarVerticalTrack
:
1280 part_id
= extra
.is_upper
? SBP_UPPERTRACKVERT
: SBP_LOWERTRACKVERT
;
1283 NOTREACHED() << "Invalid part: " << part
;
1289 state_id
= SCRBS_DISABLED
;
1292 state_id
= SCRBS_HOVER
;
1295 state_id
= SCRBS_NORMAL
;
1298 state_id
= SCRBS_PRESSED
;
1301 NOTREACHED() << "Invalid state: " << state
;
1305 if (handle
&& draw_theme_
)
1306 return draw_theme_(handle
, hdc
, part_id
, state_id
, &rect_win
, NULL
);
1308 // Draw it manually.
1309 if ((system_colors_
[COLOR_SCROLLBAR
] != system_colors_
[COLOR_3DFACE
]) &&
1310 (system_colors_
[COLOR_SCROLLBAR
] != system_colors_
[COLOR_WINDOW
])) {
1311 FillRect(hdc
, &rect_win
, reinterpret_cast<HBRUSH
>(COLOR_SCROLLBAR
+ 1));
1314 RECT align_rect
= gfx::Rect(extra
.track_x
, extra
.track_y
, extra
.track_width
,
1315 extra
.track_height
).ToRECT();
1316 SetCheckerboardShader(&paint
, align_rect
);
1317 canvas
->drawIRect(skia::RECTToSkIRect(rect_win
), paint
);
1319 if (extra
.classic_state
& DFCS_PUSHED
)
1320 InvertRect(hdc
, &rect_win
);
1324 HRESULT
NativeThemeWin::PaintSpinButton(
1328 const gfx::Rect
& rect
,
1329 const InnerSpinButtonExtraParams
& extra
) const {
1330 HANDLE handle
= GetThemeHandle(SPIN
);
1331 RECT rect_win
= rect
.ToRECT();
1332 int part_id
= extra
.spin_up
? SPNP_UP
: SPNP_DOWN
;
1336 state_id
= extra
.spin_up
? UPS_DISABLED
: DNS_DISABLED
;
1339 state_id
= extra
.spin_up
? UPS_HOT
: DNS_HOT
;
1342 state_id
= extra
.spin_up
? UPS_NORMAL
: DNS_NORMAL
;
1345 state_id
= extra
.spin_up
? UPS_PRESSED
: DNS_PRESSED
;
1348 NOTREACHED() << "Invalid state " << state
;
1352 if (handle
&& draw_theme_
)
1353 return draw_theme_(handle
, hdc
, part_id
, state_id
, &rect_win
, NULL
);
1354 DrawFrameControl(hdc
, &rect_win
, DFC_SCROLL
, extra
.classic_state
);
1358 HRESULT
NativeThemeWin::PaintTrackbar(
1363 const gfx::Rect
& rect
,
1364 const TrackbarExtraParams
& extra
) const {
1365 int part_id
= part
== kTrackbarTrack
? TKP_TRACK
: TKP_THUMBBOTTOM
;
1367 part_id
= part
== kTrackbarTrack
? TKP_TRACKVERT
: TKP_THUMBVERT
;
1372 state_id
= TUS_DISABLED
;
1378 state_id
= TUS_NORMAL
;
1381 state_id
= TUS_PRESSED
;
1384 NOTREACHED() << "Invalid state " << state
;
1388 // Make the channel be 4 px thick in the center of the supplied rect. (4 px
1389 // matches what XP does in various menus; GetThemePartSize() doesn't seem to
1390 // return good values here.)
1391 RECT rect_win
= rect
.ToRECT();
1392 RECT channel_rect
= rect
.ToRECT();
1393 const int channel_thickness
= 4;
1394 if (part_id
== TKP_TRACK
) {
1396 ((channel_rect
.bottom
- channel_rect
.top
- channel_thickness
) / 2);
1397 channel_rect
.bottom
= channel_rect
.top
+ channel_thickness
;
1398 } else if (part_id
== TKP_TRACKVERT
) {
1399 channel_rect
.left
+=
1400 ((channel_rect
.right
- channel_rect
.left
- channel_thickness
) / 2);
1401 channel_rect
.right
= channel_rect
.left
+ channel_thickness
;
1402 } // else this isn't actually a channel, so |channel_rect| == |rect|.
1404 HANDLE handle
= GetThemeHandle(TRACKBAR
);
1405 if (handle
&& draw_theme_
)
1406 return draw_theme_(handle
, hdc
, part_id
, state_id
, &channel_rect
, NULL
);
1408 // Classic mode, draw it manually.
1409 if ((part_id
== TKP_TRACK
) || (part_id
== TKP_TRACKVERT
)) {
1410 DrawEdge(hdc
, &channel_rect
, EDGE_SUNKEN
, BF_RECT
);
1411 } else if (part_id
== TKP_THUMBVERT
) {
1412 DrawEdge(hdc
, &rect_win
, EDGE_RAISED
, BF_RECT
| BF_SOFT
| BF_MIDDLE
);
1414 // Split rect into top and bottom pieces.
1415 RECT top_section
= rect
.ToRECT();
1416 RECT bottom_section
= rect
.ToRECT();
1417 top_section
.bottom
-= ((bottom_section
.right
- bottom_section
.left
) / 2);
1418 bottom_section
.top
= top_section
.bottom
;
1419 DrawEdge(hdc
, &top_section
, EDGE_RAISED
,
1420 BF_LEFT
| BF_TOP
| BF_RIGHT
| BF_SOFT
| BF_MIDDLE
| BF_ADJUST
);
1422 // Split triangular piece into two diagonals.
1423 RECT
& left_half
= bottom_section
;
1424 RECT right_half
= bottom_section
;
1425 right_half
.left
+= ((bottom_section
.right
- bottom_section
.left
) / 2);
1426 left_half
.right
= right_half
.left
;
1427 DrawEdge(hdc
, &left_half
, EDGE_RAISED
,
1428 BF_DIAGONAL_ENDTOPLEFT
| BF_SOFT
| BF_MIDDLE
| BF_ADJUST
);
1429 DrawEdge(hdc
, &right_half
, EDGE_RAISED
,
1430 BF_DIAGONAL_ENDBOTTOMLEFT
| BF_SOFT
| BF_MIDDLE
| BF_ADJUST
);
1432 // If the button is pressed, draw hatching.
1433 if (extra
.classic_state
& DFCS_PUSHED
) {
1435 SetCheckerboardShader(&paint
, rect_win
);
1437 // Fill all three pieces with the pattern.
1438 canvas
->drawIRect(skia::RECTToSkIRect(top_section
), paint
);
1440 SkScalar left_triangle_top
= SkIntToScalar(left_half
.top
);
1441 SkScalar left_triangle_right
= SkIntToScalar(left_half
.right
);
1442 SkPath left_triangle
;
1443 left_triangle
.moveTo(SkIntToScalar(left_half
.left
), left_triangle_top
);
1444 left_triangle
.lineTo(left_triangle_right
, left_triangle_top
);
1445 left_triangle
.lineTo(left_triangle_right
,
1446 SkIntToScalar(left_half
.bottom
));
1447 left_triangle
.close();
1448 canvas
->drawPath(left_triangle
, paint
);
1450 SkScalar right_triangle_left
= SkIntToScalar(right_half
.left
);
1451 SkScalar right_triangle_top
= SkIntToScalar(right_half
.top
);
1452 SkPath right_triangle
;
1453 right_triangle
.moveTo(right_triangle_left
, right_triangle_top
);
1454 right_triangle
.lineTo(SkIntToScalar(right_half
.right
),
1455 right_triangle_top
);
1456 right_triangle
.lineTo(right_triangle_left
,
1457 SkIntToScalar(right_half
.bottom
));
1458 right_triangle
.close();
1459 canvas
->drawPath(right_triangle
, paint
);
1465 HRESULT
NativeThemeWin::PaintProgressBar(
1467 const gfx::Rect
& rect
,
1468 const ProgressBarExtraParams
& extra
) const {
1469 // There is no documentation about the animation speed, frame-rate, nor
1470 // size of moving overlay of the indeterminate progress bar.
1471 // So we just observed real-world programs and guessed following parameters.
1472 const int kDeteminateOverlayPixelsPerSecond
= 300;
1473 const int kDeteminateOverlayWidth
= 120;
1474 const int kIndeterminateOverlayPixelsPerSecond
= 175;
1475 const int kVistaIndeterminateOverlayWidth
= 120;
1476 const int kXPIndeterminateOverlayWidth
= 55;
1477 // The thickness of the bar frame inside |value_rect|
1478 const int kXPBarPadding
= 3;
1480 RECT bar_rect
= rect
.ToRECT();
1481 RECT value_rect
= gfx::Rect(extra
.value_rect_x
,
1483 extra
.value_rect_width
,
1484 extra
.value_rect_height
).ToRECT();
1486 bool pre_vista
= base::win::GetVersion() < base::win::VERSION_VISTA
;
1487 HANDLE handle
= GetThemeHandle(PROGRESS
);
1488 if (handle
&& draw_theme_
&& draw_theme_ex_
) {
1489 draw_theme_(handle
, hdc
, PP_BAR
, 0, &bar_rect
, NULL
);
1491 int bar_width
= bar_rect
.right
- bar_rect
.left
;
1492 if (extra
.determinate
) {
1493 // TODO(morrita): this RTL guess can be wrong.
1494 // We should pass the direction from WebKit side.
1495 bool is_rtl
= (bar_rect
.right
== value_rect
.right
&&
1496 bar_rect
.left
!= value_rect
.left
);
1497 // We should care the direction here because PP_CNUNK painting
1499 DTBGOPTS value_draw_options
;
1500 value_draw_options
.dwSize
= sizeof(DTBGOPTS
);
1501 value_draw_options
.dwFlags
= is_rtl
? DTBG_MIRRORDC
: 0;
1502 value_draw_options
.rcClip
= bar_rect
;
1505 // On XP, progress bar is chunk-style and has no glossy effect.
1506 // We need to shrink destination rect to fit the part inside the bar
1507 // with an appropriate margin.
1508 RECT shrunk_value_rect
= InsetRect(&value_rect
, kXPBarPadding
);
1509 draw_theme_ex_(handle
, hdc
, PP_CHUNK
, 0,
1510 &shrunk_value_rect
, &value_draw_options
);
1512 // On Vista or later, the progress bar part has a
1513 // single-block value part. It also has glossy effect.
1514 // And the value part has exactly same height as the bar part
1515 // so we don't need to shrink the rect.
1516 draw_theme_ex_(handle
, hdc
, PP_FILL
, 0,
1517 &value_rect
, &value_draw_options
);
1519 int dx
= ComputeAnimationProgress(bar_width
,
1520 kDeteminateOverlayWidth
,
1521 kDeteminateOverlayPixelsPerSecond
,
1522 extra
.animated_seconds
);
1523 RECT overlay_rect
= value_rect
;
1524 overlay_rect
.left
+= dx
;
1525 overlay_rect
.right
= overlay_rect
.left
+ kDeteminateOverlayWidth
;
1526 draw_theme_(handle
, hdc
, PP_MOVEOVERLAY
, 0, &overlay_rect
, &value_rect
);
1529 // A glossy overlay for indeterminate progress bar has small pause
1530 // after each animation. We emulate this by adding an invisible margin
1531 // the animation has to traverse.
1532 int width_with_margin
= bar_width
+ kIndeterminateOverlayPixelsPerSecond
;
1533 int overlay_width
= pre_vista
?
1534 kXPIndeterminateOverlayWidth
: kVistaIndeterminateOverlayWidth
;
1535 int dx
= ComputeAnimationProgress(width_with_margin
,
1537 kIndeterminateOverlayPixelsPerSecond
,
1538 extra
.animated_seconds
);
1539 RECT overlay_rect
= bar_rect
;
1540 overlay_rect
.left
+= dx
;
1541 overlay_rect
.right
= overlay_rect
.left
+ overlay_width
;
1543 RECT shrunk_rect
= InsetRect(&overlay_rect
, kXPBarPadding
);
1544 RECT shrunk_bar_rect
= InsetRect(&bar_rect
, kXPBarPadding
);
1545 draw_theme_(handle
, hdc
, PP_CHUNK
, 0, &shrunk_rect
, &shrunk_bar_rect
);
1547 draw_theme_(handle
, hdc
, PP_MOVEOVERLAY
, 0, &overlay_rect
, &bar_rect
);
1554 HBRUSH bg_brush
= GetSysColorBrush(COLOR_BTNFACE
);
1555 HBRUSH fg_brush
= GetSysColorBrush(COLOR_BTNSHADOW
);
1556 FillRect(hdc
, &bar_rect
, bg_brush
);
1557 FillRect(hdc
, &value_rect
, fg_brush
);
1558 DrawEdge(hdc
, &bar_rect
, EDGE_SUNKEN
, BF_RECT
| BF_ADJUST
);
1562 HRESULT
NativeThemeWin::PaintWindowResizeGripper(HDC hdc
,
1563 const gfx::Rect
& rect
) const {
1564 HANDLE handle
= GetThemeHandle(STATUS
);
1565 RECT rect_win
= rect
.ToRECT();
1566 if (handle
&& draw_theme_
) {
1567 // Paint the status bar gripper. There doesn't seem to be a
1568 // standard gripper in Windows for the space between
1569 // scrollbars. This is pretty close, but it's supposed to be
1570 // painted over a status bar.
1571 return draw_theme_(handle
, hdc
, SP_GRIPPER
, 0, &rect_win
, NULL
);
1574 // Draw a windows classic scrollbar gripper.
1575 DrawFrameControl(hdc
, &rect_win
, DFC_SCROLL
, DFCS_SCROLLSIZEGRIP
);
1579 HRESULT
NativeThemeWin::PaintTabPanelBackground(HDC hdc
,
1580 const gfx::Rect
& rect
) const {
1581 HANDLE handle
= GetThemeHandle(TAB
);
1582 RECT rect_win
= rect
.ToRECT();
1583 if (handle
&& draw_theme_
)
1584 return draw_theme_(handle
, hdc
, TABP_BODY
, 0, &rect_win
, NULL
);
1586 // Classic just renders a flat color background.
1587 FillRect(hdc
, &rect_win
, reinterpret_cast<HBRUSH
>(COLOR_3DFACE
+ 1));
1591 HRESULT
NativeThemeWin::PaintTextField(
1595 const gfx::Rect
& rect
,
1596 const TextFieldExtraParams
& extra
) const {
1597 int part_id
= EP_EDITTEXT
;
1598 int state_id
= ETS_NORMAL
;
1601 if (extra
.is_read_only
) {
1602 state_id
= ETS_READONLY
;
1603 } else if (extra
.is_focused
) {
1604 state_id
= ETS_FOCUSED
;
1606 state_id
= ETS_NORMAL
;
1613 state_id
= ETS_SELECTED
;
1616 state_id
= ETS_DISABLED
;
1619 NOTREACHED() << "Invalid state: " << state
;
1623 RECT rect_win
= rect
.ToRECT();
1624 return PaintTextField(hdc
, part_id
, state_id
, extra
.classic_state
,
1626 skia::SkColorToCOLORREF(extra
.background_color
),
1627 extra
.fill_content_area
, extra
.draw_edges
);
1630 HRESULT
NativeThemeWin::PaintTextField(HDC hdc
,
1636 bool fill_content_area
,
1637 bool draw_edges
) const {
1638 // TODO(ojan): http://b/1210017 Figure out how to give the ability to
1639 // exclude individual edges from being drawn.
1641 HANDLE handle
= GetThemeHandle(TEXTFIELD
);
1642 // TODO(mpcomplete): can we detect if the color is specified by the user,
1643 // and if not, just use the system color?
1644 // CreateSolidBrush() accepts a RGB value but alpha must be 0.
1645 HBRUSH bg_brush
= CreateSolidBrush(color
);
1647 // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible
1648 // draw_theme_ex_ is NULL and draw_theme_ is non-null.
1649 if (handle
&& (draw_theme_ex_
|| (draw_theme_
&& draw_edges
))) {
1650 if (draw_theme_ex_
) {
1651 static const DTBGOPTS omit_border_options
= {
1656 const DTBGOPTS
* draw_opts
= draw_edges
? NULL
: &omit_border_options
;
1657 hr
= draw_theme_ex_(handle
, hdc
, part_id
, state_id
, rect
, draw_opts
);
1659 hr
= draw_theme_(handle
, hdc
, part_id
, state_id
, rect
, NULL
);
1662 // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
1663 if (fill_content_area
&& get_theme_content_rect_
) {
1665 hr
= get_theme_content_rect_(handle
, hdc
, part_id
, state_id
, rect
,
1667 FillRect(hdc
, &content_rect
, bg_brush
);
1670 // Draw it manually.
1672 DrawEdge(hdc
, rect
, EDGE_SUNKEN
, BF_RECT
| BF_ADJUST
);
1674 if (fill_content_area
) {
1675 FillRect(hdc
, rect
, (classic_state
& DFCS_INACTIVE
) ?
1676 reinterpret_cast<HBRUSH
>(COLOR_BTNFACE
+ 1) : bg_brush
);
1680 DeleteObject(bg_brush
);
1684 HRESULT
NativeThemeWin::PaintScaledTheme(HANDLE theme
,
1688 const gfx::Rect
& rect
) const {
1689 // Correct the scaling and positioning of sub-components such as scrollbar
1690 // arrows and thumb grippers in the event that the world transform applies
1691 // scaling (e.g. in high-DPI mode).
1692 XFORM save_transform
;
1693 if (GetWorldTransform(hdc
, &save_transform
)) {
1694 float scale
= save_transform
.eM11
;
1695 if (scale
!= 1 && save_transform
.eM12
== 0) {
1696 ModifyWorldTransform(hdc
, NULL
, MWT_IDENTITY
);
1697 gfx::Rect scaled_rect
= gfx::ToEnclosedRect(
1698 gfx::ScaleRect(rect
, scale
));
1699 RECT bounds
= gfx::Rect(scaled_rect
.x() + save_transform
.eDx
,
1700 scaled_rect
.y() + save_transform
.eDy
,
1701 scaled_rect
.width(),
1702 scaled_rect
.height()).ToRECT();
1703 HRESULT result
= draw_theme_(theme
, hdc
, part_id
, state_id
, &bounds
,
1705 SetWorldTransform(hdc
, &save_transform
);
1709 RECT bounds
= rect
.ToRECT();
1710 return draw_theme_(theme
, hdc
, part_id
, state_id
, &bounds
, NULL
);
1714 NativeThemeWin::ThemeName
NativeThemeWin::GetThemeName(Part part
) {
1722 case kInnerSpinButton
:
1726 case kMenuPopupGutter
:
1728 case kMenuPopupArrow
:
1729 case kMenuPopupSeparator
:
1735 case kScrollbarDownArrow
:
1736 case kScrollbarLeftArrow
:
1737 case kScrollbarRightArrow
:
1738 case kScrollbarUpArrow
:
1739 case kScrollbarHorizontalThumb
:
1740 case kScrollbarVerticalThumb
:
1741 case kScrollbarHorizontalTrack
:
1742 case kScrollbarVerticalTrack
:
1752 case kWindowResizeGripper
:
1756 NOTREACHED() << "Invalid part: " << part
;
1763 int NativeThemeWin::GetWindowsPart(Part part
,
1765 const ExtraParams
& extra
) {
1769 part_id
= BP_CHECKBOX
;
1772 part_id
= MENU_POPUPCHECK
;
1774 case kMenuPopupArrow
:
1775 part_id
= MENU_POPUPSUBMENU
;
1777 case kMenuPopupGutter
:
1778 part_id
= MENU_POPUPGUTTER
;
1780 case kMenuPopupSeparator
:
1781 part_id
= MENU_POPUPSEPARATOR
;
1784 part_id
= BP_PUSHBUTTON
;
1787 part_id
= BP_RADIOBUTTON
;
1789 case kWindowResizeGripper
:
1790 part_id
= SP_GRIPPER
;
1792 case kScrollbarDownArrow
:
1793 case kScrollbarLeftArrow
:
1794 case kScrollbarRightArrow
:
1795 case kScrollbarUpArrow
:
1796 part_id
= SBP_ARROWBTN
;
1798 case kScrollbarHorizontalThumb
:
1799 part_id
= SBP_THUMBBTNHORZ
;
1801 case kScrollbarVerticalThumb
:
1802 part_id
= SBP_THUMBBTNVERT
;
1805 NOTREACHED() << "Invalid part: " << part
;
1811 int NativeThemeWin::GetWindowsState(Part part
,
1813 const ExtraParams
& extra
) {
1819 state_id
= CBS_UNCHECKEDNORMAL
;
1822 state_id
= CBS_UNCHECKEDHOT
;
1825 state_id
= CBS_UNCHECKEDPRESSED
;
1828 state_id
= CBS_UNCHECKEDDISABLED
;
1831 NOTREACHED() << "Invalid state: " << state
;
1840 state_id
= extra
.menu_check
.is_radio
? MC_BULLETNORMAL
1841 : MC_CHECKMARKNORMAL
;
1844 state_id
= extra
.menu_check
.is_radio
? MC_BULLETDISABLED
1845 : MC_CHECKMARKDISABLED
;
1848 NOTREACHED() << "Invalid state: " << state
;
1852 case kMenuPopupArrow
:
1853 case kMenuPopupGutter
:
1854 case kMenuPopupSeparator
:
1857 state_id
= MBI_NORMAL
;
1863 state_id
= MBI_PUSHED
;
1866 state_id
= MBI_DISABLED
;
1869 NOTREACHED() << "Invalid state: " << state
;
1876 state_id
= PBS_NORMAL
;
1882 state_id
= PBS_PRESSED
;
1885 state_id
= PBS_DISABLED
;
1888 NOTREACHED() << "Invalid state: " << state
;
1895 state_id
= RBS_UNCHECKEDNORMAL
;
1898 state_id
= RBS_UNCHECKEDHOT
;
1901 state_id
= RBS_UNCHECKEDPRESSED
;
1904 state_id
= RBS_UNCHECKEDDISABLED
;
1907 NOTREACHED() << "Invalid state: " << state
;
1911 case kWindowResizeGripper
:
1917 state_id
= 1; // gripper has no windows state
1920 NOTREACHED() << "Invalid state: " << state
;
1924 case kScrollbarDownArrow
:
1927 state_id
= ABS_DOWNNORMAL
;
1930 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1931 state_id
= base::win::GetVersion() < base::win::VERSION_VISTA
?
1932 ABS_DOWNHOT
: ABS_DOWNHOVER
;
1935 state_id
= ABS_DOWNPRESSED
;
1938 state_id
= ABS_DOWNDISABLED
;
1941 NOTREACHED() << "Invalid state: " << state
;
1945 case kScrollbarLeftArrow
:
1948 state_id
= ABS_LEFTNORMAL
;
1951 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1952 state_id
= base::win::GetVersion() < base::win::VERSION_VISTA
?
1953 ABS_LEFTHOT
: ABS_LEFTHOVER
;
1956 state_id
= ABS_LEFTPRESSED
;
1959 state_id
= ABS_LEFTDISABLED
;
1962 NOTREACHED() << "Invalid state: " << state
;
1966 case kScrollbarRightArrow
:
1969 state_id
= ABS_RIGHTNORMAL
;
1972 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1973 state_id
= base::win::GetVersion() < base::win::VERSION_VISTA
?
1974 ABS_RIGHTHOT
: ABS_RIGHTHOVER
;
1977 state_id
= ABS_RIGHTPRESSED
;
1980 state_id
= ABS_RIGHTDISABLED
;
1983 NOTREACHED() << "Invalid state: " << state
;
1987 case kScrollbarUpArrow
:
1990 state_id
= ABS_UPNORMAL
;
1993 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1994 state_id
= base::win::GetVersion() < base::win::VERSION_VISTA
?
1995 ABS_UPHOT
: ABS_UPHOVER
;
1998 state_id
= ABS_UPPRESSED
;
2001 state_id
= ABS_UPDISABLED
;
2004 NOTREACHED() << "Invalid state: " << state
;
2008 case kScrollbarHorizontalThumb
:
2009 case kScrollbarVerticalThumb
:
2012 state_id
= SCRBS_NORMAL
;
2015 // Mimic WebKit's behaviour in ScrollbarThemeChromiumWin.cpp.
2016 state_id
= base::win::GetVersion() < base::win::VERSION_VISTA
?
2017 SCRBS_HOT
: SCRBS_HOVER
;
2020 state_id
= SCRBS_PRESSED
;
2023 state_id
= SCRBS_DISABLED
;
2026 NOTREACHED() << "Invalid state: " << state
;
2031 NOTREACHED() << "Invalid part: " << part
;
2037 HRESULT
NativeThemeWin::GetThemeInt(ThemeName theme
,
2042 HANDLE handle
= GetThemeHandle(theme
);
2043 if (handle
&& get_theme_int_
)
2044 return get_theme_int_(handle
, part_id
, state_id
, prop_id
, value
);
2048 HRESULT
NativeThemeWin::PaintFrameControl(HDC hdc
,
2049 const gfx::Rect
& rect
,
2053 State control_state
) const {
2054 const int width
= rect
.width();
2055 const int height
= rect
.height();
2057 // DrawFrameControl for menu arrow/check wants a monochrome bitmap.
2058 base::win::ScopedBitmap
mask_bitmap(CreateBitmap(width
, height
, 1, 1, NULL
));
2060 if (mask_bitmap
== NULL
)
2061 return E_OUTOFMEMORY
;
2063 base::win::ScopedCreateDC
bitmap_dc(CreateCompatibleDC(NULL
));
2064 base::win::ScopedSelectObject
select_bitmap(bitmap_dc
, mask_bitmap
);
2065 RECT local_rect
= { 0, 0, width
, height
};
2066 DrawFrameControl(bitmap_dc
, &local_rect
, type
, state
);
2068 // We're going to use BitBlt with a b&w mask. This results in using the dest
2069 // dc's text color for the black bits in the mask, and the dest dc's
2070 // background color for the white bits in the mask. DrawFrameControl draws the
2071 // check in black, and the background in white.
2074 switch (control_state
) {
2075 case NativeTheme::kHovered
:
2076 bg_color_key
= COLOR_HIGHLIGHT
;
2077 text_color_key
= COLOR_HIGHLIGHTTEXT
;
2079 case NativeTheme::kNormal
:
2080 bg_color_key
= COLOR_MENU
;
2081 text_color_key
= COLOR_MENUTEXT
;
2083 case NativeTheme::kDisabled
:
2084 bg_color_key
= is_selected
? COLOR_HIGHLIGHT
: COLOR_MENU
;
2085 text_color_key
= COLOR_GRAYTEXT
;
2089 bg_color_key
= COLOR_MENU
;
2090 text_color_key
= COLOR_MENUTEXT
;
2093 COLORREF old_bg_color
= SetBkColor(hdc
, GetSysColor(bg_color_key
));
2094 COLORREF old_text_color
= SetTextColor(hdc
, GetSysColor(text_color_key
));
2095 BitBlt(hdc
, rect
.x(), rect
.y(), width
, height
, bitmap_dc
, 0, 0, SRCCOPY
);
2096 SetBkColor(hdc
, old_bg_color
);
2097 SetTextColor(hdc
, old_text_color
);
2102 HANDLE
NativeThemeWin::GetThemeHandle(ThemeName theme_name
) const {
2103 if (!open_theme_
|| theme_name
< 0 || theme_name
>= LAST
)
2106 if (theme_handles_
[theme_name
])
2107 return theme_handles_
[theme_name
];
2109 // Not found, try to load it.
2111 switch (theme_name
) {
2113 handle
= open_theme_(NULL
, L
"Button");
2116 handle
= open_theme_(NULL
, L
"Listview");
2119 handle
= open_theme_(NULL
, L
"Menu");
2122 handle
= open_theme_(NULL
, L
"Combobox");
2125 handle
= open_theme_(NULL
, L
"Scrollbar");
2128 handle
= open_theme_(NULL
, L
"Status");
2131 handle
= open_theme_(NULL
, L
"Tab");
2134 handle
= open_theme_(NULL
, L
"Edit");
2137 handle
= open_theme_(NULL
, L
"Trackbar");
2140 handle
= open_theme_(NULL
, L
"Window");
2143 handle
= open_theme_(NULL
, L
"Progress");
2146 handle
= open_theme_(NULL
, L
"Spin");
2151 theme_handles_
[theme_name
] = handle
;