Added llvm into exceptions as we can't add README.chromium into 3rd party repository
[chromium-blink-merge.git] / ui / native_theme / native_theme_win.cc
blob4429d6ba71e138521b4c631906b49c0620562c31
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"
7 #include <windows.h>
8 #include <uxtheme.h>
9 #include <vsstyle.h>
10 #include <vssym32.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/geometry/rect.h"
28 #include "ui/gfx/geometry/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
35 #endif
37 namespace {
39 // TODO: Obtain the correct colors using GetSysColor.
40 // Theme colors returned by GetSystemColor().
41 const SkColor kInvalidColorIdColor = SkColorSetRGB(255, 0, 128);
42 // Dialogs:
43 const SkColor kDialogBackgroundColor = SkColorSetRGB(251, 251, 251);
44 // FocusableBorder:
45 const SkColor kFocusedBorderColor = SkColorSetRGB(0x4d, 0x90, 0xfe);
46 const SkColor kUnfocusedBorderColor = SkColorSetRGB(0xd9, 0xd9, 0xd9);
47 // Button:
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);
52 // MenuItem:
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);
57 // Table:
58 const SkColor kTreeSelectionBackgroundUnfocused = SkColorSetRGB(240, 240, 240);
60 // Windows system color IDs cached and updated by the native theme.
61 const int kSystemColors[] = {
62 COLOR_3DFACE,
63 COLOR_BTNTEXT,
64 COLOR_GRAYTEXT,
65 COLOR_HIGHLIGHT,
66 COLOR_HIGHLIGHTTEXT,
67 COLOR_SCROLLBAR,
68 COLOR_WINDOW,
69 COLOR_WINDOWTEXT,
70 COLOR_BTNFACE,
71 COLOR_MENUHIGHLIGHT,
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);
86 SkBitmap temp_bitmap;
87 temp_bitmap.installPixels(info, buffer, info.minRowBytes());
88 SkBitmap bitmap;
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,
99 &local_matrix));
100 paint->setShader(shader.get());
103 // <-a->
104 // [ ***** ]
105 // ____ | |
106 // <-a-> <------b----->
107 // a: object_width
108 // b: frame_width
109 // *: animating object
111 // - the animation goes from "[" to "]" repeatedly.
112 // - the animation offset is at first "|"
114 int ComputeAnimationProgress(int frame_width,
115 int object_width,
116 int pixels_per_second,
117 double animated_seconds) {
118 int animation_width = frame_width + object_width;
119 double interval = static_cast<double>(animation_width) / pixels_per_second;
120 double ratio = fmod(animated_seconds, interval) / interval;
121 return static_cast<int>(animation_width * ratio) - object_width;
124 RECT InsetRect(const RECT* rect, int size) {
125 gfx::Rect result(*rect);
126 result.Inset(size, size);
127 return result.ToRECT();
130 } // namespace
132 namespace ui {
134 bool NativeThemeWin::IsThemingActive() const {
135 return is_theme_active_ && is_theme_active_();
138 bool NativeThemeWin::IsUsingHighContrastTheme() const {
139 if (is_using_high_contrast_valid_)
140 return is_using_high_contrast_;
141 HIGHCONTRAST result;
142 result.cbSize = sizeof(HIGHCONTRAST);
143 is_using_high_contrast_ =
144 SystemParametersInfo(SPI_GETHIGHCONTRAST, result.cbSize, &result, 0) &&
145 (result.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON;
146 is_using_high_contrast_valid_ = true;
147 return is_using_high_contrast_;
150 HRESULT NativeThemeWin::GetThemeColor(ThemeName theme,
151 int part_id,
152 int state_id,
153 int prop_id,
154 SkColor* color) const {
155 HANDLE handle = GetThemeHandle(theme);
156 if (!handle || !get_theme_color_)
157 return E_NOTIMPL;
158 COLORREF color_ref;
159 if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) != S_OK)
160 return E_NOTIMPL;
161 *color = skia::COLORREFToSkColor(color_ref);
162 return S_OK;
165 SkColor NativeThemeWin::GetThemeColorWithDefault(ThemeName theme,
166 int part_id,
167 int state_id,
168 int prop_id,
169 int default_sys_color) const {
170 SkColor color;
171 return (GetThemeColor(theme, part_id, state_id, prop_id, &color) == S_OK) ?
172 color : color_utils::GetSysSkColor(default_sys_color);
175 gfx::Size NativeThemeWin::GetThemeBorderSize(ThemeName theme) const {
176 // For simplicity use the wildcard state==0, part==0, since it works
177 // for the cases we currently depend on.
178 int border;
179 return (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK) ?
180 gfx::Size(border, border) :
181 gfx::Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE));
184 void NativeThemeWin::DisableTheming() const {
185 if (set_theme_properties_)
186 set_theme_properties_(0);
189 void NativeThemeWin::CloseHandles() const {
190 if (!close_theme_)
191 return;
193 for (int i = 0; i < LAST; ++i) {
194 if (theme_handles_[i]) {
195 close_theme_(theme_handles_[i]);
196 theme_handles_[i] = NULL;
201 bool NativeThemeWin::IsClassicTheme(ThemeName name) const {
202 return !theme_dll_ || !GetThemeHandle(name);
205 // static
206 NativeThemeWin* NativeThemeWin::instance() {
207 CR_DEFINE_STATIC_LOCAL(NativeThemeWin, s_native_theme, ());
208 return &s_native_theme;
211 gfx::Size NativeThemeWin::GetPartSize(Part part,
212 State state,
213 const ExtraParams& extra) const {
214 gfx::Size part_size = CommonThemeGetPartSize(part, state, extra);
215 if (!part_size.IsEmpty())
216 return part_size;
218 // The GetThemePartSize call below returns the default size without
219 // accounting for user customization (crbug/218291).
220 switch (part) {
221 case kScrollbarDownArrow:
222 case kScrollbarLeftArrow:
223 case kScrollbarRightArrow:
224 case kScrollbarUpArrow:
225 case kScrollbarHorizontalThumb:
226 case kScrollbarVerticalThumb:
227 case kScrollbarHorizontalTrack:
228 case kScrollbarVerticalTrack: {
229 int size = gfx::win::GetSystemMetricsInDIP(SM_CXVSCROLL);
230 if (size == 0)
231 size = 17;
232 return gfx::Size(size, size);
234 default:
235 break;
238 int part_id = GetWindowsPart(part, state, extra);
239 int state_id = GetWindowsState(part, state, extra);
241 base::win::ScopedGetDC screen_dc(NULL);
242 SIZE size;
243 if (SUCCEEDED(GetThemePartSize(GetThemeName(part), screen_dc, part_id,
244 state_id, NULL, TS_TRUE, &size)))
245 return gfx::Size(size.cx, size.cy);
247 // TODO(rogerta): For now, we need to support radio buttons and checkboxes
248 // when theming is not enabled. Support for other parts can be added
249 // if/when needed.
250 return (part == kCheckbox || part == kRadio) ?
251 gfx::Size(13, 13) : gfx::Size();
254 void NativeThemeWin::Paint(SkCanvas* canvas,
255 Part part,
256 State state,
257 const gfx::Rect& rect,
258 const ExtraParams& extra) const {
259 if (rect.IsEmpty())
260 return;
262 switch (part) {
263 case kComboboxArrow:
264 CommonThemePaintComboboxArrow(canvas, rect);
265 return;
266 case kMenuPopupGutter:
267 CommonThemePaintMenuGutter(canvas, rect);
268 return;
269 case kMenuPopupSeparator:
270 CommonThemePaintMenuSeparator(canvas, rect);
271 return;
272 case kMenuPopupBackground:
273 CommonThemePaintMenuBackground(canvas, rect);
274 return;
275 case kMenuItemBackground:
276 CommonThemePaintMenuItemBackground(canvas, state, rect);
277 return;
278 default:
279 break;
282 bool needs_paint_indirect = false;
283 if (!skia::SupportsPlatformPaint(canvas)) {
284 // This block will only get hit with --enable-accelerated-drawing flag.
285 needs_paint_indirect = true;
286 } else {
287 // Scrollbar components on Windows Classic theme (on all Windows versions)
288 // have particularly problematic alpha values, so always draw them
289 // indirectly. In addition, scrollbar thumbs and grippers for the Windows XP
290 // theme (available only on Windows XP) also need their alpha values
291 // fixed.
292 switch (part) {
293 case kScrollbarDownArrow:
294 case kScrollbarUpArrow:
295 case kScrollbarLeftArrow:
296 case kScrollbarRightArrow:
297 needs_paint_indirect = !GetThemeHandle(SCROLLBAR);
298 break;
299 case kScrollbarHorizontalThumb:
300 case kScrollbarVerticalThumb:
301 case kScrollbarHorizontalGripper:
302 case kScrollbarVerticalGripper:
303 needs_paint_indirect = !GetThemeHandle(SCROLLBAR) ||
304 base::win::GetVersion() == base::win::VERSION_XP;
305 break;
306 default:
307 break;
311 if (needs_paint_indirect)
312 PaintIndirect(canvas, part, state, rect, extra);
313 else
314 PaintDirect(canvas, part, state, rect, extra);
317 NativeThemeWin::NativeThemeWin()
318 : theme_dll_(LoadLibrary(L"uxtheme.dll")),
319 draw_theme_(NULL),
320 draw_theme_ex_(NULL),
321 get_theme_color_(NULL),
322 get_theme_content_rect_(NULL),
323 get_theme_part_size_(NULL),
324 open_theme_(NULL),
325 close_theme_(NULL),
326 set_theme_properties_(NULL),
327 is_theme_active_(NULL),
328 get_theme_int_(NULL),
329 color_change_listener_(this),
330 is_using_high_contrast_(false),
331 is_using_high_contrast_valid_(false) {
332 if (theme_dll_) {
333 draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>(
334 GetProcAddress(theme_dll_, "DrawThemeBackground"));
335 draw_theme_ex_ = reinterpret_cast<DrawThemeBackgroundExPtr>(
336 GetProcAddress(theme_dll_, "DrawThemeBackgroundEx"));
337 get_theme_color_ = reinterpret_cast<GetThemeColorPtr>(
338 GetProcAddress(theme_dll_, "GetThemeColor"));
339 get_theme_content_rect_ = reinterpret_cast<GetThemeContentRectPtr>(
340 GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect"));
341 get_theme_part_size_ = reinterpret_cast<GetThemePartSizePtr>(
342 GetProcAddress(theme_dll_, "GetThemePartSize"));
343 open_theme_ = reinterpret_cast<OpenThemeDataPtr>(
344 GetProcAddress(theme_dll_, "OpenThemeData"));
345 close_theme_ = reinterpret_cast<CloseThemeDataPtr>(
346 GetProcAddress(theme_dll_, "CloseThemeData"));
347 set_theme_properties_ = reinterpret_cast<SetThemeAppPropertiesPtr>(
348 GetProcAddress(theme_dll_, "SetThemeAppProperties"));
349 is_theme_active_ = reinterpret_cast<IsThemeActivePtr>(
350 GetProcAddress(theme_dll_, "IsThemeActive"));
351 get_theme_int_ = reinterpret_cast<GetThemeIntPtr>(
352 GetProcAddress(theme_dll_, "GetThemeInt"));
354 memset(theme_handles_, 0, sizeof(theme_handles_));
356 // Initialize the cached system colors.
357 UpdateSystemColors();
360 NativeThemeWin::~NativeThemeWin() {
361 if (theme_dll_) {
362 // todo (cpu): fix this soon. Making a call to CloseHandles() here breaks
363 // certain tests and the reliability bots.
364 // CloseHandles();
365 FreeLibrary(theme_dll_);
369 void NativeThemeWin::OnSysColorChange() {
370 UpdateSystemColors();
371 is_using_high_contrast_valid_ = false;
372 NotifyObservers();
375 void NativeThemeWin::UpdateSystemColors() {
376 for (int i = 0; i < arraysize(kSystemColors); ++i) {
377 system_colors_[kSystemColors[i]] =
378 color_utils::GetSysSkColor(kSystemColors[i]);
382 void NativeThemeWin::PaintDirect(SkCanvas* canvas,
383 Part part,
384 State state,
385 const gfx::Rect& rect,
386 const ExtraParams& extra) const {
387 skia::ScopedPlatformPaint scoped_platform_paint(canvas);
388 HDC hdc = scoped_platform_paint.GetPlatformSurface();
390 switch (part) {
391 case kCheckbox:
392 PaintCheckbox(hdc, part, state, rect, extra.button);
393 return;
394 case kInnerSpinButton:
395 PaintSpinButton(hdc, part, state, rect, extra.inner_spin);
396 return;
397 case kMenuList:
398 PaintMenuList(hdc, state, rect, extra.menu_list);
399 return;
400 case kMenuCheck:
401 PaintMenuCheck(hdc, state, rect, extra.menu_check);
402 return;
403 case kMenuCheckBackground:
404 PaintMenuCheckBackground(hdc, state, rect);
405 return;
406 case kMenuPopupArrow:
407 PaintMenuArrow(hdc, state, rect, extra.menu_arrow);
408 return;
409 case kMenuPopupBackground:
410 PaintMenuBackground(hdc, rect);
411 return;
412 case kMenuPopupGutter:
413 PaintMenuGutter(hdc, rect);
414 return;
415 case kMenuPopupSeparator:
416 PaintMenuSeparator(hdc, rect);
417 return;
418 case kMenuItemBackground:
419 PaintMenuItemBackground(hdc, state, rect, extra.menu_item);
420 return;
421 case kProgressBar:
422 PaintProgressBar(hdc, rect, extra.progress_bar);
423 return;
424 case kPushButton:
425 PaintPushButton(hdc, part, state, rect, extra.button);
426 return;
427 case kRadio:
428 PaintRadioButton(hdc, part, state, rect, extra.button);
429 return;
430 case kScrollbarDownArrow:
431 case kScrollbarUpArrow:
432 case kScrollbarLeftArrow:
433 case kScrollbarRightArrow:
434 PaintScrollbarArrow(hdc, part, state, rect, extra.scrollbar_arrow);
435 return;
436 case kScrollbarHorizontalThumb:
437 case kScrollbarVerticalThumb:
438 case kScrollbarHorizontalGripper:
439 case kScrollbarVerticalGripper:
440 PaintScrollbarThumb(hdc, part, state, rect, extra.scrollbar_thumb);
441 return;
442 case kScrollbarHorizontalTrack:
443 case kScrollbarVerticalTrack:
444 PaintScrollbarTrack(canvas, hdc, part, state, rect,
445 extra.scrollbar_track);
446 return;
447 case kScrollbarCorner:
448 canvas->drawColor(SK_ColorWHITE, SkXfermode::kSrc_Mode);
449 return;
450 case kTabPanelBackground:
451 PaintTabPanelBackground(hdc, rect);
452 return;
453 case kTextField:
454 PaintTextField(hdc, part, state, rect, extra.text_field);
455 return;
456 case kTrackbarThumb:
457 case kTrackbarTrack:
458 PaintTrackbar(canvas, hdc, part, state, rect, extra.trackbar);
459 return;
460 case kWindowResizeGripper:
461 PaintWindowResizeGripper(hdc, rect);
462 return;
463 case kComboboxArrow:
464 case kSliderTrack:
465 case kSliderThumb:
466 case kMaxPart:
467 NOTREACHED();
471 SkColor NativeThemeWin::GetSystemColor(ColorId color_id) const {
472 SkColor color;
473 if (CommonThemeGetSystemColor(color_id, &color))
474 return color;
476 switch (color_id) {
477 // Windows
478 case kColorId_WindowBackground:
479 return system_colors_[COLOR_WINDOW];
481 // Dialogs
482 case kColorId_DialogBackground:
483 return gfx::IsInvertedColorScheme() ?
484 color_utils::InvertColor(kDialogBackgroundColor) :
485 kDialogBackgroundColor;
487 // FocusableBorder
488 case kColorId_FocusedBorderColor:
489 return kFocusedBorderColor;
490 case kColorId_UnfocusedBorderColor:
491 return kUnfocusedBorderColor;
493 // Button
494 case kColorId_ButtonBackgroundColor:
495 return kButtonBackgroundColor;
496 case kColorId_ButtonEnabledColor:
497 return system_colors_[COLOR_BTNTEXT];
498 case kColorId_ButtonDisabledColor:
499 return system_colors_[COLOR_GRAYTEXT];
500 case kColorId_ButtonHighlightColor:
501 return kButtonHighlightColor;
502 case kColorId_ButtonHoverColor:
503 return kButtonHoverColor;
504 case kColorId_ButtonHoverBackgroundColor:
505 return kButtonHoverBackgroundColor;
506 case kColorId_BlueButtonEnabledColor:
507 case kColorId_BlueButtonDisabledColor:
508 case kColorId_BlueButtonPressedColor:
509 case kColorId_BlueButtonHoverColor:
510 NOTREACHED();
511 return kInvalidColorIdColor;
513 // MenuItem
514 case kColorId_EnabledMenuItemForegroundColor:
515 return kEnabledMenuItemForegroundColor;
516 case kColorId_DisabledMenuItemForegroundColor:
517 return kDisabledMenuItemForegroundColor;
518 case kColorId_DisabledEmphasizedMenuItemForegroundColor:
519 return SK_ColorBLACK;
520 case kColorId_FocusedMenuItemBackgroundColor:
521 return kFocusedMenuItemBackgroundColor;
522 case kColorId_MenuSeparatorColor:
523 return kMenuSeparatorColor;
524 case kColorId_SelectedMenuItemForegroundColor:
525 case kColorId_HoverMenuItemBackgroundColor:
526 case kColorId_MenuBackgroundColor:
527 case kColorId_MenuBorderColor:
528 NOTREACHED();
529 return kInvalidColorIdColor;
531 // MenuButton
532 case kColorId_EnabledMenuButtonBorderColor:
533 case kColorId_FocusedMenuButtonBorderColor:
534 case kColorId_HoverMenuButtonBorderColor:
535 NOTREACHED();
536 return kInvalidColorIdColor;
538 // Label
539 case kColorId_LabelEnabledColor:
540 return system_colors_[COLOR_BTNTEXT];
541 case kColorId_LabelDisabledColor:
542 return system_colors_[COLOR_GRAYTEXT];
543 case kColorId_LabelBackgroundColor:
544 return system_colors_[COLOR_WINDOW];
546 // Textfield
547 case kColorId_TextfieldDefaultColor:
548 return system_colors_[COLOR_WINDOWTEXT];
549 case kColorId_TextfieldDefaultBackground:
550 return system_colors_[COLOR_WINDOW];
551 case kColorId_TextfieldReadOnlyColor:
552 return system_colors_[COLOR_GRAYTEXT];
553 case kColorId_TextfieldReadOnlyBackground:
554 return system_colors_[COLOR_3DFACE];
555 case kColorId_TextfieldSelectionColor:
556 return system_colors_[COLOR_HIGHLIGHTTEXT];
557 case kColorId_TextfieldSelectionBackgroundFocused:
558 return system_colors_[COLOR_HIGHLIGHT];
560 // Tooltip
561 case kColorId_TooltipBackground:
562 case kColorId_TooltipText:
563 NOTREACHED();
564 return kInvalidColorIdColor;
566 // Tree
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];
584 // Table
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];
601 // Results Tables
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);
645 NOTREACHED();
646 return kInvalidColorIdColor;
649 void NativeThemeWin::PaintIndirect(SkCanvas* canvas,
650 Part part,
651 State state,
652 const gfx::Rect& rect,
653 const ExtraParams& extra) const {
654 // TODO(asvitkine): This path is pretty inefficient - for each paint operation
655 // it creates a new offscreen bitmap Skia canvas. This can
656 // be sped up by doing it only once per part/state and
657 // keeping a cache of the resulting bitmaps.
659 // Create an offscreen canvas that is backed by an HDC.
660 skia::RefPtr<skia::BitmapPlatformDevice> device = skia::AdoptRef(
661 skia::BitmapPlatformDevice::Create(
662 rect.width(), rect.height(), false, NULL));
663 DCHECK(device);
664 SkCanvas offscreen_canvas(device.get());
665 DCHECK(skia::SupportsPlatformPaint(&offscreen_canvas));
667 // Some of the Windows theme drawing operations do not write correct alpha
668 // values for fully-opaque pixels; instead the pixels get alpha 0. This is
669 // especially a problem on Windows XP or when using the Classic theme.
671 // To work-around this, mark all pixels with a placeholder value, to detect
672 // which pixels get touched by the paint operation. After paint, set any
673 // pixels that have alpha 0 to opaque and placeholders to fully-transparent.
674 const SkColor placeholder = SkColorSetARGB(1, 0, 0, 0);
675 offscreen_canvas.clear(placeholder);
677 // Offset destination rects to have origin (0,0).
678 gfx::Rect adjusted_rect(rect.size());
679 ExtraParams adjusted_extra(extra);
680 switch (part) {
681 case kProgressBar:
682 adjusted_extra.progress_bar.value_rect_x = 0;
683 adjusted_extra.progress_bar.value_rect_y = 0;
684 break;
685 case kScrollbarHorizontalTrack:
686 case kScrollbarVerticalTrack:
687 adjusted_extra.scrollbar_track.track_x = 0;
688 adjusted_extra.scrollbar_track.track_y = 0;
689 break;
690 default:
691 break;
693 // Draw the theme controls using existing HDC-drawing code.
694 PaintDirect(&offscreen_canvas, part, state, adjusted_rect, adjusted_extra);
696 // Copy the pixels to a bitmap that has ref-counted pixel storage, which is
697 // necessary to have when drawing to a SkPicture.
698 const SkBitmap& hdc_bitmap =
699 offscreen_canvas.getDevice()->accessBitmap(false);
700 SkBitmap bitmap;
701 hdc_bitmap.copyTo(&bitmap, kN32_SkColorType);
703 // Post-process the pixels to fix up the alpha values (see big comment above).
704 const SkPMColor placeholder_value = SkPreMultiplyColor(placeholder);
705 const int pixel_count = rect.width() * rect.height();
706 SkPMColor* pixels = bitmap.getAddr32(0, 0);
707 for (int i = 0; i < pixel_count; i++) {
708 if (pixels[i] == placeholder_value) {
709 // Pixel wasn't touched - make it fully transparent.
710 pixels[i] = SkPackARGB32(0, 0, 0, 0);
711 } else if (SkGetPackedA32(pixels[i]) == 0) {
712 // Pixel was touched but has incorrect alpha of 0, make it fully opaque.
713 pixels[i] = SkPackARGB32(0xFF,
714 SkGetPackedR32(pixels[i]),
715 SkGetPackedG32(pixels[i]),
716 SkGetPackedB32(pixels[i]));
720 // Draw the offscreen bitmap to the destination canvas.
721 canvas->drawBitmap(bitmap, rect.x(), rect.y());
724 HRESULT NativeThemeWin::GetThemePartSize(ThemeName theme_name,
725 HDC hdc,
726 int part_id,
727 int state_id,
728 RECT* rect,
729 int ts,
730 SIZE* size) const {
731 HANDLE handle = GetThemeHandle(theme_name);
732 return (handle && get_theme_part_size_) ?
733 get_theme_part_size_(handle, hdc, part_id, state_id, rect, ts, size) :
734 E_NOTIMPL;
737 HRESULT NativeThemeWin::PaintButton(HDC hdc,
738 State state,
739 const ButtonExtraParams& extra,
740 int part_id,
741 int state_id,
742 RECT* rect) const {
743 HANDLE handle = GetThemeHandle(BUTTON);
744 if (handle && draw_theme_)
745 return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
747 // Adjust classic_state based on part, state, and extras.
748 int classic_state = extra.classic_state;
749 switch (part_id) {
750 case BP_CHECKBOX:
751 classic_state |= DFCS_BUTTONCHECK;
752 break;
753 case BP_RADIOBUTTON:
754 classic_state |= DFCS_BUTTONRADIO;
755 break;
756 case BP_PUSHBUTTON:
757 classic_state |= DFCS_BUTTONPUSH;
758 break;
759 default:
760 NOTREACHED();
761 break;
764 switch (state) {
765 case kDisabled:
766 classic_state |= DFCS_INACTIVE;
767 break;
768 case kHovered:
769 case kNormal:
770 break;
771 case kPressed:
772 classic_state |= DFCS_PUSHED;
773 break;
774 case kNumStates:
775 NOTREACHED();
776 break;
779 if (extra.checked)
780 classic_state |= DFCS_CHECKED;
782 // Draw it manually.
783 // All pressed states have both low bits set, and no other states do.
784 const bool focused = ((state_id & ETS_FOCUSED) == ETS_FOCUSED);
785 const bool pressed = ((state_id & PBS_PRESSED) == PBS_PRESSED);
786 if ((BP_PUSHBUTTON == part_id) && (pressed || focused)) {
787 // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the
788 // button itself is shrunk by 1 pixel.
789 HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW);
790 if (brush) {
791 FrameRect(hdc, rect, brush);
792 InflateRect(rect, -1, -1);
795 DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state);
797 // Draw the focus rectangle (the dotted line box) only on buttons. For radio
798 // and checkboxes, we let webkit draw the focus rectangle (orange glow).
799 if ((BP_PUSHBUTTON == part_id) && focused) {
800 // The focus rect is inside the button. The exact number of pixels depends
801 // on whether we're in classic mode or using uxtheme.
802 if (handle && get_theme_content_rect_) {
803 get_theme_content_rect_(handle, hdc, part_id, state_id, rect, rect);
804 } else {
805 InflateRect(rect, -GetSystemMetrics(SM_CXEDGE),
806 -GetSystemMetrics(SM_CYEDGE));
808 DrawFocusRect(hdc, rect);
811 // Classic theme doesn't support indeterminate checkboxes. We draw
812 // a recangle inside a checkbox like IE10 does.
813 if (part_id == BP_CHECKBOX && extra.indeterminate) {
814 RECT inner_rect = *rect;
815 // "4 / 13" is same as IE10 in classic theme.
816 int padding = (inner_rect.right - inner_rect.left) * 4 / 13;
817 InflateRect(&inner_rect, -padding, -padding);
818 int color_index = state == kDisabled ? COLOR_GRAYTEXT : COLOR_WINDOWTEXT;
819 FillRect(hdc, &inner_rect, GetSysColorBrush(color_index));
822 return S_OK;
825 HRESULT NativeThemeWin::PaintMenuSeparator(
826 HDC hdc,
827 const gfx::Rect& rect) const {
828 RECT rect_win = rect.ToRECT();
830 HANDLE handle = GetThemeHandle(MENU);
831 if (handle && draw_theme_) {
832 // Delta is needed for non-classic to move separator up slightly.
833 --rect_win.top;
834 --rect_win.bottom;
835 return draw_theme_(handle, hdc, MENU_POPUPSEPARATOR, MPI_NORMAL, &rect_win,
836 NULL);
839 DrawEdge(hdc, &rect_win, EDGE_ETCHED, BF_TOP);
840 return S_OK;
843 HRESULT NativeThemeWin::PaintMenuGutter(HDC hdc,
844 const gfx::Rect& rect) const {
845 RECT rect_win = rect.ToRECT();
846 HANDLE handle = GetThemeHandle(MENU);
847 return (handle && draw_theme_) ?
848 draw_theme_(handle, hdc, MENU_POPUPGUTTER, MPI_NORMAL, &rect_win, NULL) :
849 E_NOTIMPL;
852 HRESULT NativeThemeWin::PaintMenuArrow(
853 HDC hdc,
854 State state,
855 const gfx::Rect& rect,
856 const MenuArrowExtraParams& extra) const {
857 int state_id = MSM_NORMAL;
858 if (state == kDisabled)
859 state_id = MSM_DISABLED;
861 HANDLE handle = GetThemeHandle(MENU);
862 RECT rect_win = rect.ToRECT();
863 if (handle && draw_theme_) {
864 if (extra.pointing_right) {
865 return draw_theme_(handle, hdc, MENU_POPUPSUBMENU, state_id, &rect_win,
866 NULL);
868 // There is no way to tell the uxtheme API to draw a left pointing arrow; it
869 // doesn't have a flag equivalent to DFCS_MENUARROWRIGHT. But they are
870 // needed for RTL locales on Vista. So use a memory DC and mirror the
871 // region with GDI's StretchBlt.
872 gfx::Rect r(rect);
873 base::win::ScopedCreateDC mem_dc(CreateCompatibleDC(hdc));
874 base::win::ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(),
875 r.height()));
876 base::win::ScopedSelectObject select_bitmap(mem_dc.Get(), mem_bitmap);
877 // Copy and horizontally mirror the background from hdc into mem_dc. Use
878 // a negative-width source rect, starting at the rightmost pixel.
879 StretchBlt(mem_dc.Get(), 0, 0, r.width(), r.height(),
880 hdc, r.right()-1, r.y(), -r.width(), r.height(), SRCCOPY);
881 // Draw the arrow.
882 RECT theme_rect = {0, 0, r.width(), r.height()};
883 HRESULT result = draw_theme_(handle, mem_dc.Get(), MENU_POPUPSUBMENU,
884 state_id, &theme_rect, NULL);
885 // Copy and mirror the result back into mem_dc.
886 StretchBlt(hdc, r.x(), r.y(), r.width(), r.height(),
887 mem_dc.Get(), r.width()-1, 0, -r.width(), r.height(), SRCCOPY);
888 return result;
891 // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
892 // left pointing arrow. This makes the following statement counterintuitive.
893 UINT pfc_state = extra.pointing_right ? DFCS_MENUARROW : DFCS_MENUARROWRIGHT;
894 return PaintFrameControl(hdc, rect, DFC_MENU, pfc_state, extra.is_selected,
895 state);
898 HRESULT NativeThemeWin::PaintMenuBackground(HDC hdc,
899 const gfx::Rect& rect) const {
900 HANDLE handle = GetThemeHandle(MENU);
901 RECT rect_win = rect.ToRECT();
902 if (handle && draw_theme_) {
903 HRESULT result = draw_theme_(handle, hdc, MENU_POPUPBACKGROUND, 0,
904 &rect_win, NULL);
905 FrameRect(hdc, &rect_win, GetSysColorBrush(COLOR_3DSHADOW));
906 return result;
909 FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_MENU));
910 DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT);
911 return S_OK;
914 HRESULT NativeThemeWin::PaintMenuCheck(
915 HDC hdc,
916 State state,
917 const gfx::Rect& rect,
918 const MenuCheckExtraParams& extra) const {
919 HANDLE handle = GetThemeHandle(MENU);
920 if (handle && draw_theme_) {
921 const int state_id = extra.is_radio ?
922 ((state == kDisabled) ? MC_BULLETDISABLED : MC_BULLETNORMAL) :
923 ((state == kDisabled) ? MC_CHECKMARKDISABLED : MC_CHECKMARKNORMAL);
924 RECT rect_win = rect.ToRECT();
925 return draw_theme_(handle, hdc, MENU_POPUPCHECK, state_id, &rect_win, NULL);
928 return PaintFrameControl(hdc, rect, DFC_MENU,
929 extra.is_radio ? DFCS_MENUBULLET : DFCS_MENUCHECK,
930 extra.is_selected, state);
933 HRESULT NativeThemeWin::PaintMenuCheckBackground(HDC hdc,
934 State state,
935 const gfx::Rect& rect) const {
936 HANDLE handle = GetThemeHandle(MENU);
937 if (!handle || !draw_theme_)
938 return S_OK; // Nothing to do for background.
940 int state_id = state == kDisabled ? MCB_DISABLED : MCB_NORMAL;
941 RECT rect_win = rect.ToRECT();
942 return draw_theme_(handle, hdc, MENU_POPUPCHECKBACKGROUND, state_id,
943 &rect_win, NULL);
946 HRESULT NativeThemeWin::PaintMenuItemBackground(
947 HDC hdc,
948 State state,
949 const gfx::Rect& rect,
950 const MenuItemExtraParams& extra) const {
951 HANDLE handle = GetThemeHandle(MENU);
952 RECT rect_win = rect.ToRECT();
953 int state_id = MPI_NORMAL;
954 switch (state) {
955 case kDisabled:
956 state_id = extra.is_selected ? MPI_DISABLEDHOT : MPI_DISABLED;
957 break;
958 case kHovered:
959 state_id = MPI_HOT;
960 break;
961 case kNormal:
962 break;
963 case kPressed:
964 case kNumStates:
965 NOTREACHED();
966 break;
969 if (handle && draw_theme_)
970 return draw_theme_(handle, hdc, MENU_POPUPITEM, state_id, &rect_win, NULL);
972 if (extra.is_selected)
973 FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_HIGHLIGHT));
974 return S_OK;
977 HRESULT NativeThemeWin::PaintPushButton(HDC hdc,
978 Part part,
979 State state,
980 const gfx::Rect& rect,
981 const ButtonExtraParams& extra) const {
982 int state_id = extra.is_default ? PBS_DEFAULTED : PBS_NORMAL;
983 switch (state) {
984 case kDisabled:
985 state_id = PBS_DISABLED;
986 break;
987 case kHovered:
988 state_id = PBS_HOT;
989 break;
990 case kNormal:
991 break;
992 case kPressed:
993 state_id = PBS_PRESSED;
994 break;
995 case kNumStates:
996 NOTREACHED();
997 break;
1000 RECT rect_win = rect.ToRECT();
1001 return PaintButton(hdc, state, extra, BP_PUSHBUTTON, state_id, &rect_win);
1004 HRESULT NativeThemeWin::PaintRadioButton(HDC hdc,
1005 Part part,
1006 State state,
1007 const gfx::Rect& rect,
1008 const ButtonExtraParams& extra) const {
1009 int state_id = extra.checked ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL;
1010 switch (state) {
1011 case kDisabled:
1012 state_id = extra.checked ? RBS_CHECKEDDISABLED : RBS_UNCHECKEDDISABLED;
1013 break;
1014 case kHovered:
1015 state_id = extra.checked ? RBS_CHECKEDHOT : RBS_UNCHECKEDHOT;
1016 break;
1017 case kNormal:
1018 break;
1019 case kPressed:
1020 state_id = extra.checked ? RBS_CHECKEDPRESSED : RBS_UNCHECKEDPRESSED;
1021 break;
1022 case kNumStates:
1023 NOTREACHED();
1024 break;
1027 RECT rect_win = rect.ToRECT();
1028 return PaintButton(hdc, state, extra, BP_RADIOBUTTON, state_id, &rect_win);
1031 HRESULT NativeThemeWin::PaintCheckbox(HDC hdc,
1032 Part part,
1033 State state,
1034 const gfx::Rect& rect,
1035 const ButtonExtraParams& extra) const {
1036 int state_id = extra.checked ?
1037 CBS_CHECKEDNORMAL :
1038 (extra.indeterminate ? CBS_MIXEDNORMAL : CBS_UNCHECKEDNORMAL);
1039 switch (state) {
1040 case kDisabled:
1041 state_id = extra.checked ?
1042 CBS_CHECKEDDISABLED :
1043 (extra.indeterminate ? CBS_MIXEDDISABLED : CBS_UNCHECKEDDISABLED);
1044 break;
1045 case kHovered:
1046 state_id = extra.checked ?
1047 CBS_CHECKEDHOT :
1048 (extra.indeterminate ? CBS_MIXEDHOT : CBS_UNCHECKEDHOT);
1049 break;
1050 case kNormal:
1051 break;
1052 case kPressed:
1053 state_id = extra.checked ?
1054 CBS_CHECKEDPRESSED :
1055 (extra.indeterminate ? CBS_MIXEDPRESSED : CBS_UNCHECKEDPRESSED);
1056 break;
1057 case kNumStates:
1058 NOTREACHED();
1059 break;
1062 RECT rect_win = rect.ToRECT();
1063 return PaintButton(hdc, state, extra, BP_CHECKBOX, state_id, &rect_win);
1066 HRESULT NativeThemeWin::PaintMenuList(HDC hdc,
1067 State state,
1068 const gfx::Rect& rect,
1069 const MenuListExtraParams& extra) const {
1070 HANDLE handle = GetThemeHandle(MENULIST);
1071 RECT rect_win = rect.ToRECT();
1072 int state_id = CBXS_NORMAL;
1073 switch (state) {
1074 case kDisabled:
1075 state_id = CBXS_DISABLED;
1076 break;
1077 case kHovered:
1078 state_id = CBXS_HOT;
1079 break;
1080 case kNormal:
1081 break;
1082 case kPressed:
1083 state_id = CBXS_PRESSED;
1084 break;
1085 case kNumStates:
1086 NOTREACHED();
1087 break;
1090 if (handle && draw_theme_)
1091 return draw_theme_(handle, hdc, CP_DROPDOWNBUTTON, state_id, &rect_win,
1092 NULL);
1094 // Draw it manually.
1095 DrawFrameControl(hdc, &rect_win, DFC_SCROLL,
1096 DFCS_SCROLLCOMBOBOX | extra.classic_state);
1097 return S_OK;
1100 HRESULT NativeThemeWin::PaintScrollbarArrow(
1101 HDC hdc,
1102 Part part,
1103 State state,
1104 const gfx::Rect& rect,
1105 const ScrollbarArrowExtraParams& extra) const {
1106 static const int state_id_matrix[4][kNumStates] = {
1107 ABS_DOWNDISABLED, ABS_DOWNHOT, ABS_DOWNNORMAL, ABS_DOWNPRESSED,
1108 ABS_LEFTDISABLED, ABS_LEFTHOT, ABS_LEFTNORMAL, ABS_LEFTPRESSED,
1109 ABS_RIGHTDISABLED, ABS_RIGHTHOT, ABS_RIGHTNORMAL, ABS_RIGHTPRESSED,
1110 ABS_UPDISABLED, ABS_UPHOT, ABS_UPNORMAL, ABS_UPPRESSED
1112 HANDLE handle = GetThemeHandle(SCROLLBAR);
1113 RECT rect_win = rect.ToRECT();
1114 if (handle && draw_theme_) {
1115 int index = part - kScrollbarDownArrow;
1116 DCHECK_GE(index, 0);
1117 DCHECK_LT(static_cast<size_t>(index), arraysize(state_id_matrix));
1118 int state_id = state_id_matrix[index][state];
1120 // Hovering means that the cursor is over the scroolbar, but not over the
1121 // specific arrow itself. We don't want to show it "hot" mode, but only
1122 // in "hover" mode.
1123 if (state == kHovered && extra.is_hovering) {
1124 switch (part) {
1125 case kScrollbarDownArrow:
1126 state_id = ABS_DOWNHOVER;
1127 break;
1128 case kScrollbarLeftArrow:
1129 state_id = ABS_LEFTHOVER;
1130 break;
1131 case kScrollbarRightArrow:
1132 state_id = ABS_RIGHTHOVER;
1133 break;
1134 case kScrollbarUpArrow:
1135 state_id = ABS_UPHOVER;
1136 break;
1137 default:
1138 NOTREACHED();
1139 break;
1142 return PaintScaledTheme(handle, hdc, SBP_ARROWBTN, state_id, rect);
1145 int classic_state = DFCS_SCROLLDOWN;
1146 switch (part) {
1147 case kScrollbarDownArrow:
1148 break;
1149 case kScrollbarLeftArrow:
1150 classic_state = DFCS_SCROLLLEFT;
1151 break;
1152 case kScrollbarRightArrow:
1153 classic_state = DFCS_SCROLLRIGHT;
1154 break;
1155 case kScrollbarUpArrow:
1156 classic_state = DFCS_SCROLLUP;
1157 break;
1158 default:
1159 NOTREACHED();
1160 break;
1162 switch (state) {
1163 case kDisabled:
1164 classic_state |= DFCS_INACTIVE;
1165 break;
1166 case kHovered:
1167 classic_state |= DFCS_HOT;
1168 break;
1169 case kNormal:
1170 break;
1171 case kPressed:
1172 classic_state |= DFCS_PUSHED;
1173 break;
1174 case kNumStates:
1175 NOTREACHED();
1176 break;
1178 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, classic_state);
1179 return S_OK;
1182 HRESULT NativeThemeWin::PaintScrollbarThumb(
1183 HDC hdc,
1184 Part part,
1185 State state,
1186 const gfx::Rect& rect,
1187 const ScrollbarThumbExtraParams& extra) const {
1188 HANDLE handle = GetThemeHandle(SCROLLBAR);
1189 RECT rect_win = rect.ToRECT();
1191 int part_id = SBP_THUMBBTNVERT;
1192 switch (part) {
1193 case kScrollbarHorizontalThumb:
1194 part_id = SBP_THUMBBTNHORZ;
1195 break;
1196 case kScrollbarVerticalThumb:
1197 break;
1198 case kScrollbarHorizontalGripper:
1199 part_id = SBP_GRIPPERHORZ;
1200 break;
1201 case kScrollbarVerticalGripper:
1202 part_id = SBP_GRIPPERVERT;
1203 break;
1204 default:
1205 NOTREACHED();
1206 break;
1209 int state_id = SCRBS_NORMAL;
1210 switch (state) {
1211 case kDisabled:
1212 state_id = SCRBS_DISABLED;
1213 break;
1214 case kHovered:
1215 state_id = extra.is_hovering ? SCRBS_HOVER : SCRBS_HOT;
1216 break;
1217 case kNormal:
1218 break;
1219 case kPressed:
1220 state_id = SCRBS_PRESSED;
1221 break;
1222 case kNumStates:
1223 NOTREACHED();
1224 break;
1227 if (handle && draw_theme_)
1228 return PaintScaledTheme(handle, hdc, part_id, state_id, rect);
1230 // Draw it manually.
1231 if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT))
1232 DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_MIDDLE);
1233 // Classic mode doesn't have a gripper.
1234 return S_OK;
1237 HRESULT NativeThemeWin::PaintScrollbarTrack(
1238 SkCanvas* canvas,
1239 HDC hdc,
1240 Part part,
1241 State state,
1242 const gfx::Rect& rect,
1243 const ScrollbarTrackExtraParams& extra) const {
1244 HANDLE handle = GetThemeHandle(SCROLLBAR);
1245 RECT rect_win = rect.ToRECT();
1247 const int part_id = extra.is_upper ?
1248 ((part == kScrollbarHorizontalTrack) ?
1249 SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT) :
1250 ((part == kScrollbarHorizontalTrack) ?
1251 SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT);
1253 int state_id = SCRBS_NORMAL;
1254 switch (state) {
1255 case kDisabled:
1256 state_id = SCRBS_DISABLED;
1257 break;
1258 case kHovered:
1259 state_id = SCRBS_HOVER;
1260 break;
1261 case kNormal:
1262 break;
1263 case kPressed:
1264 state_id = SCRBS_PRESSED;
1265 break;
1266 case kNumStates:
1267 NOTREACHED();
1268 break;
1271 if (handle && draw_theme_)
1272 return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
1274 // Draw it manually.
1275 if ((system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_3DFACE]) &&
1276 (system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_WINDOW])) {
1277 FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_SCROLLBAR + 1));
1278 } else {
1279 SkPaint paint;
1280 RECT align_rect = gfx::Rect(extra.track_x, extra.track_y, extra.track_width,
1281 extra.track_height).ToRECT();
1282 SetCheckerboardShader(&paint, align_rect);
1283 canvas->drawIRect(skia::RECTToSkIRect(rect_win), paint);
1285 if (extra.classic_state & DFCS_PUSHED)
1286 InvertRect(hdc, &rect_win);
1287 return S_OK;
1290 HRESULT NativeThemeWin::PaintSpinButton(
1291 HDC hdc,
1292 Part part,
1293 State state,
1294 const gfx::Rect& rect,
1295 const InnerSpinButtonExtraParams& extra) const {
1296 HANDLE handle = GetThemeHandle(SPIN);
1297 RECT rect_win = rect.ToRECT();
1298 int part_id = extra.spin_up ? SPNP_UP : SPNP_DOWN;
1299 int state_id = extra.spin_up ? UPS_NORMAL : DNS_NORMAL;
1300 switch (state) {
1301 case kDisabled:
1302 state_id = extra.spin_up ? UPS_DISABLED : DNS_DISABLED;
1303 break;
1304 case kHovered:
1305 state_id = extra.spin_up ? UPS_HOT : DNS_HOT;
1306 break;
1307 case kNormal:
1308 break;
1309 case kPressed:
1310 state_id = extra.spin_up ? UPS_PRESSED : DNS_PRESSED;
1311 break;
1312 case kNumStates:
1313 NOTREACHED();
1314 break;
1317 if (handle && draw_theme_)
1318 return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
1319 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, extra.classic_state);
1320 return S_OK;
1323 HRESULT NativeThemeWin::PaintTrackbar(
1324 SkCanvas* canvas,
1325 HDC hdc,
1326 Part part,
1327 State state,
1328 const gfx::Rect& rect,
1329 const TrackbarExtraParams& extra) const {
1330 const int part_id = extra.vertical ?
1331 ((part == kTrackbarTrack) ? TKP_TRACKVERT : TKP_THUMBVERT) :
1332 ((part == kTrackbarTrack) ? TKP_TRACK : TKP_THUMBBOTTOM);
1334 int state_id = TUS_NORMAL;
1335 switch (state) {
1336 case kDisabled:
1337 state_id = TUS_DISABLED;
1338 break;
1339 case kHovered:
1340 state_id = TUS_HOT;
1341 break;
1342 case kNormal:
1343 break;
1344 case kPressed:
1345 state_id = TUS_PRESSED;
1346 break;
1347 case kNumStates:
1348 NOTREACHED();
1349 break;
1352 // Make the channel be 4 px thick in the center of the supplied rect. (4 px
1353 // matches what XP does in various menus; GetThemePartSize() doesn't seem to
1354 // return good values here.)
1355 RECT rect_win = rect.ToRECT();
1356 RECT channel_rect = rect.ToRECT();
1357 const int channel_thickness = 4;
1358 if (part_id == TKP_TRACK) {
1359 channel_rect.top +=
1360 ((channel_rect.bottom - channel_rect.top - channel_thickness) / 2);
1361 channel_rect.bottom = channel_rect.top + channel_thickness;
1362 } else if (part_id == TKP_TRACKVERT) {
1363 channel_rect.left +=
1364 ((channel_rect.right - channel_rect.left - channel_thickness) / 2);
1365 channel_rect.right = channel_rect.left + channel_thickness;
1366 } // else this isn't actually a channel, so |channel_rect| == |rect|.
1368 HANDLE handle = GetThemeHandle(TRACKBAR);
1369 if (handle && draw_theme_)
1370 return draw_theme_(handle, hdc, part_id, state_id, &channel_rect, NULL);
1372 // Classic mode, draw it manually.
1373 if ((part_id == TKP_TRACK) || (part_id == TKP_TRACKVERT)) {
1374 DrawEdge(hdc, &channel_rect, EDGE_SUNKEN, BF_RECT);
1375 } else if (part_id == TKP_THUMBVERT) {
1376 DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE);
1377 } else {
1378 // Split rect into top and bottom pieces.
1379 RECT top_section = rect.ToRECT();
1380 RECT bottom_section = rect.ToRECT();
1381 top_section.bottom -= ((bottom_section.right - bottom_section.left) / 2);
1382 bottom_section.top = top_section.bottom;
1383 DrawEdge(hdc, &top_section, EDGE_RAISED,
1384 BF_LEFT | BF_TOP | BF_RIGHT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1386 // Split triangular piece into two diagonals.
1387 RECT& left_half = bottom_section;
1388 RECT right_half = bottom_section;
1389 right_half.left += ((bottom_section.right - bottom_section.left) / 2);
1390 left_half.right = right_half.left;
1391 DrawEdge(hdc, &left_half, EDGE_RAISED,
1392 BF_DIAGONAL_ENDTOPLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1393 DrawEdge(hdc, &right_half, EDGE_RAISED,
1394 BF_DIAGONAL_ENDBOTTOMLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1396 // If the button is pressed, draw hatching.
1397 if (extra.classic_state & DFCS_PUSHED) {
1398 SkPaint paint;
1399 SetCheckerboardShader(&paint, rect_win);
1401 // Fill all three pieces with the pattern.
1402 canvas->drawIRect(skia::RECTToSkIRect(top_section), paint);
1404 SkScalar left_triangle_top = SkIntToScalar(left_half.top);
1405 SkScalar left_triangle_right = SkIntToScalar(left_half.right);
1406 SkPath left_triangle;
1407 left_triangle.moveTo(SkIntToScalar(left_half.left), left_triangle_top);
1408 left_triangle.lineTo(left_triangle_right, left_triangle_top);
1409 left_triangle.lineTo(left_triangle_right,
1410 SkIntToScalar(left_half.bottom));
1411 left_triangle.close();
1412 canvas->drawPath(left_triangle, paint);
1414 SkScalar right_triangle_left = SkIntToScalar(right_half.left);
1415 SkScalar right_triangle_top = SkIntToScalar(right_half.top);
1416 SkPath right_triangle;
1417 right_triangle.moveTo(right_triangle_left, right_triangle_top);
1418 right_triangle.lineTo(SkIntToScalar(right_half.right),
1419 right_triangle_top);
1420 right_triangle.lineTo(right_triangle_left,
1421 SkIntToScalar(right_half.bottom));
1422 right_triangle.close();
1423 canvas->drawPath(right_triangle, paint);
1426 return S_OK;
1429 HRESULT NativeThemeWin::PaintProgressBar(
1430 HDC hdc,
1431 const gfx::Rect& rect,
1432 const ProgressBarExtraParams& extra) const {
1433 // There is no documentation about the animation speed, frame-rate, nor
1434 // size of moving overlay of the indeterminate progress bar.
1435 // So we just observed real-world programs and guessed following parameters.
1436 const int kDeterminateOverlayPixelsPerSecond = 300;
1437 const int kDeterminateOverlayWidth = 120;
1438 const int kIndeterminateOverlayPixelsPerSecond = 175;
1439 const int kVistaIndeterminateOverlayWidth = 120;
1440 const int kXPIndeterminateOverlayWidth = 55;
1441 // The thickness of the bar frame inside |value_rect|
1442 const int kXPBarPadding = 3;
1444 RECT bar_rect = rect.ToRECT();
1445 RECT value_rect = gfx::Rect(extra.value_rect_x,
1446 extra.value_rect_y,
1447 extra.value_rect_width,
1448 extra.value_rect_height).ToRECT();
1450 HANDLE handle = GetThemeHandle(PROGRESS);
1451 if (!handle || !draw_theme_ || !draw_theme_ex_) {
1452 FillRect(hdc, &bar_rect, GetSysColorBrush(COLOR_BTNFACE));
1453 FillRect(hdc, &value_rect, GetSysColorBrush(COLOR_BTNSHADOW));
1454 DrawEdge(hdc, &bar_rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1455 return S_OK;
1458 draw_theme_(handle, hdc, PP_BAR, 0, &bar_rect, NULL);
1460 bool pre_vista = base::win::GetVersion() < base::win::VERSION_VISTA;
1461 int bar_width = bar_rect.right - bar_rect.left;
1462 if (!extra.determinate) {
1463 // The glossy overlay for the indeterminate progress bar has a small pause
1464 // after each animation. We emulate this by adding an invisible margin the
1465 // animation has to traverse.
1466 int width_with_margin = bar_width + kIndeterminateOverlayPixelsPerSecond;
1467 int overlay_width = pre_vista ?
1468 kXPIndeterminateOverlayWidth : kVistaIndeterminateOverlayWidth;
1469 RECT overlay_rect = bar_rect;
1470 overlay_rect.left += ComputeAnimationProgress(
1471 width_with_margin, overlay_width, kIndeterminateOverlayPixelsPerSecond,
1472 extra.animated_seconds);
1473 overlay_rect.right = overlay_rect.left + overlay_width;
1474 if (pre_vista) {
1475 RECT shrunk_rect = InsetRect(&overlay_rect, kXPBarPadding);
1476 RECT shrunk_bar_rect = InsetRect(&bar_rect, kXPBarPadding);
1477 draw_theme_(handle, hdc, PP_CHUNK, 0, &shrunk_rect, &shrunk_bar_rect);
1478 } else {
1479 draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &bar_rect);
1481 return S_OK;
1484 // We care about the direction here because PP_CHUNK painting is asymmetric.
1485 // TODO(morrita): This RTL guess can be wrong. We should pass in the
1486 // direction from WebKit.
1487 const DTBGOPTS value_draw_options = {
1488 sizeof(DTBGOPTS),
1489 (bar_rect.right == value_rect.right && bar_rect.left != value_rect.left) ?
1490 DTBG_MIRRORDC : 0,
1491 bar_rect
1493 if (pre_vista) {
1494 // On XP, the progress bar is chunk-style and has no glossy effect. We need
1495 // to shrink the destination rect to fit the part inside the bar with an
1496 // appropriate margin.
1497 RECT shrunk_value_rect = InsetRect(&value_rect, kXPBarPadding);
1498 draw_theme_ex_(handle, hdc, PP_CHUNK, 0, &shrunk_value_rect,
1499 &value_draw_options);
1500 } else {
1501 // On Vista or later, the progress bar part has a single-block value part
1502 // and a glossy effect. The value part has exactly same height as the bar
1503 // part, so we don't need to shrink the rect.
1504 draw_theme_ex_(handle, hdc, PP_FILL, 0, &value_rect, &value_draw_options);
1506 RECT overlay_rect = value_rect;
1507 overlay_rect.left += ComputeAnimationProgress(
1508 bar_width, kDeterminateOverlayWidth, kDeterminateOverlayPixelsPerSecond,
1509 extra.animated_seconds);
1510 overlay_rect.right = overlay_rect.left + kDeterminateOverlayWidth;
1511 draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &value_rect);
1513 return S_OK;
1516 HRESULT NativeThemeWin::PaintWindowResizeGripper(HDC hdc,
1517 const gfx::Rect& rect) const {
1518 HANDLE handle = GetThemeHandle(STATUS);
1519 RECT rect_win = rect.ToRECT();
1520 if (handle && draw_theme_) {
1521 // Paint the status bar gripper. There doesn't seem to be a standard
1522 // gripper in Windows for the space between scrollbars. This is pretty
1523 // close, but it's supposed to be painted over a status bar.
1524 return draw_theme_(handle, hdc, SP_GRIPPER, 0, &rect_win, NULL);
1527 // Draw a windows classic scrollbar gripper.
1528 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
1529 return S_OK;
1532 HRESULT NativeThemeWin::PaintTabPanelBackground(HDC hdc,
1533 const gfx::Rect& rect) const {
1534 HANDLE handle = GetThemeHandle(TAB);
1535 RECT rect_win = rect.ToRECT();
1536 if (handle && draw_theme_)
1537 return draw_theme_(handle, hdc, TABP_BODY, 0, &rect_win, NULL);
1539 // Classic just renders a flat color background.
1540 FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
1541 return S_OK;
1544 HRESULT NativeThemeWin::PaintTextField(
1545 HDC hdc,
1546 Part part,
1547 State state,
1548 const gfx::Rect& rect,
1549 const TextFieldExtraParams& extra) const {
1550 int state_id = ETS_NORMAL;
1551 switch (state) {
1552 case kDisabled:
1553 state_id = ETS_DISABLED;
1554 break;
1555 case kHovered:
1556 state_id = ETS_HOT;
1557 break;
1558 case kNormal:
1559 if (extra.is_read_only)
1560 state_id = ETS_READONLY;
1561 else if (extra.is_focused)
1562 state_id = ETS_FOCUSED;
1563 break;
1564 case kPressed:
1565 state_id = ETS_SELECTED;
1566 break;
1567 case kNumStates:
1568 NOTREACHED();
1569 break;
1572 RECT rect_win = rect.ToRECT();
1573 return PaintTextField(hdc, EP_EDITTEXT, state_id, extra.classic_state,
1574 &rect_win,
1575 skia::SkColorToCOLORREF(extra.background_color),
1576 extra.fill_content_area, extra.draw_edges);
1579 HRESULT NativeThemeWin::PaintTextField(HDC hdc,
1580 int part_id,
1581 int state_id,
1582 int classic_state,
1583 RECT* rect,
1584 COLORREF color,
1585 bool fill_content_area,
1586 bool draw_edges) const {
1587 // TODO(ojan): http://b/1210017 Figure out how to give the ability to
1588 // exclude individual edges from being drawn.
1590 HANDLE handle = GetThemeHandle(TEXTFIELD);
1591 // TODO(mpcomplete): can we detect if the color is specified by the user,
1592 // and if not, just use the system color?
1593 // CreateSolidBrush() accepts a RGB value but alpha must be 0.
1594 base::win::ScopedGDIObject<HBRUSH> bg_brush(CreateSolidBrush(color));
1595 // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible
1596 // draw_theme_ex_ is NULL and draw_theme_ is non-null.
1597 if (!handle || (!draw_theme_ex_ && (!draw_theme_ || !draw_edges))) {
1598 // Draw it manually.
1599 if (draw_edges)
1600 DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1602 if (fill_content_area) {
1603 FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ?
1604 reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1) : bg_brush);
1606 return S_OK;
1609 static const DTBGOPTS omit_border_options = {
1610 sizeof(DTBGOPTS),
1611 DTBG_OMITBORDER,
1612 { 0, 0, 0, 0 }
1614 HRESULT hr = draw_theme_ex_ ?
1615 draw_theme_ex_(handle, hdc, part_id, state_id, rect,
1616 draw_edges ? NULL : &omit_border_options) :
1617 draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
1619 // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
1620 if (fill_content_area && get_theme_content_rect_) {
1621 RECT content_rect;
1622 hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect,
1623 &content_rect);
1624 FillRect(hdc, &content_rect, bg_brush);
1626 return hr;
1629 HRESULT NativeThemeWin::PaintScaledTheme(HANDLE theme,
1630 HDC hdc,
1631 int part_id,
1632 int state_id,
1633 const gfx::Rect& rect) const {
1634 // Correct the scaling and positioning of sub-components such as scrollbar
1635 // arrows and thumb grippers in the event that the world transform applies
1636 // scaling (e.g. in high-DPI mode).
1637 XFORM save_transform;
1638 if (GetWorldTransform(hdc, &save_transform)) {
1639 float scale = save_transform.eM11;
1640 if (scale != 1 && save_transform.eM12 == 0) {
1641 ModifyWorldTransform(hdc, NULL, MWT_IDENTITY);
1642 gfx::Rect scaled_rect(gfx::ToEnclosedRect(gfx::ScaleRect(rect, scale)));
1643 scaled_rect.Offset(save_transform.eDx, save_transform.eDy);
1644 RECT bounds = scaled_rect.ToRECT();
1645 HRESULT result = draw_theme_(theme, hdc, part_id, state_id, &bounds,
1646 NULL);
1647 SetWorldTransform(hdc, &save_transform);
1648 return result;
1651 RECT bounds = rect.ToRECT();
1652 return draw_theme_(theme, hdc, part_id, state_id, &bounds, NULL);
1655 // static
1656 NativeThemeWin::ThemeName NativeThemeWin::GetThemeName(Part part) {
1657 switch (part) {
1658 case kCheckbox:
1659 case kPushButton:
1660 case kRadio:
1661 return BUTTON;
1662 case kInnerSpinButton:
1663 return SPIN;
1664 case kMenuList:
1665 case kMenuCheck:
1666 case kMenuPopupArrow:
1667 case kMenuPopupGutter:
1668 case kMenuPopupSeparator:
1669 return MENU;
1670 case kProgressBar:
1671 return PROGRESS;
1672 case kScrollbarDownArrow:
1673 case kScrollbarLeftArrow:
1674 case kScrollbarRightArrow:
1675 case kScrollbarUpArrow:
1676 case kScrollbarHorizontalThumb:
1677 case kScrollbarVerticalThumb:
1678 case kScrollbarHorizontalTrack:
1679 case kScrollbarVerticalTrack:
1680 return SCROLLBAR;
1681 case kSliderTrack:
1682 case kSliderThumb:
1683 return TRACKBAR;
1684 case kTextField:
1685 return TEXTFIELD;
1686 case kWindowResizeGripper:
1687 return STATUS;
1688 case kComboboxArrow:
1689 case kMenuCheckBackground:
1690 case kMenuPopupBackground:
1691 case kMenuItemBackground:
1692 case kScrollbarHorizontalGripper:
1693 case kScrollbarVerticalGripper:
1694 case kScrollbarCorner:
1695 case kTabPanelBackground:
1696 case kTrackbarThumb:
1697 case kTrackbarTrack:
1698 case kMaxPart:
1699 NOTREACHED();
1701 return LAST;
1704 // static
1705 int NativeThemeWin::GetWindowsPart(Part part,
1706 State state,
1707 const ExtraParams& extra) {
1708 switch (part) {
1709 case kCheckbox:
1710 return BP_CHECKBOX;
1711 case kMenuCheck:
1712 return MENU_POPUPCHECK;
1713 case kMenuPopupArrow:
1714 return MENU_POPUPSUBMENU;
1715 case kMenuPopupGutter:
1716 return MENU_POPUPGUTTER;
1717 case kMenuPopupSeparator:
1718 return MENU_POPUPSEPARATOR;
1719 case kPushButton:
1720 return BP_PUSHBUTTON;
1721 case kRadio:
1722 return BP_RADIOBUTTON;
1723 case kScrollbarDownArrow:
1724 case kScrollbarLeftArrow:
1725 case kScrollbarRightArrow:
1726 case kScrollbarUpArrow:
1727 return SBP_ARROWBTN;
1728 case kScrollbarHorizontalThumb:
1729 return SBP_THUMBBTNHORZ;
1730 case kScrollbarVerticalThumb:
1731 return SBP_THUMBBTNVERT;
1732 case kWindowResizeGripper:
1733 return SP_GRIPPER;
1734 case kComboboxArrow:
1735 case kInnerSpinButton:
1736 case kMenuList:
1737 case kMenuCheckBackground:
1738 case kMenuPopupBackground:
1739 case kMenuItemBackground:
1740 case kProgressBar:
1741 case kScrollbarHorizontalTrack:
1742 case kScrollbarVerticalTrack:
1743 case kScrollbarHorizontalGripper:
1744 case kScrollbarVerticalGripper:
1745 case kScrollbarCorner:
1746 case kSliderTrack:
1747 case kSliderThumb:
1748 case kTabPanelBackground:
1749 case kTextField:
1750 case kTrackbarThumb:
1751 case kTrackbarTrack:
1752 case kMaxPart:
1753 NOTREACHED();
1755 return 0;
1758 int NativeThemeWin::GetWindowsState(Part part,
1759 State state,
1760 const ExtraParams& extra) {
1761 switch (part) {
1762 case kCheckbox:
1763 switch (state) {
1764 case kDisabled:
1765 return CBS_UNCHECKEDDISABLED;
1766 case kHovered:
1767 return CBS_UNCHECKEDHOT;
1768 case kNormal:
1769 return CBS_UNCHECKEDNORMAL;
1770 case kPressed:
1771 return CBS_UNCHECKEDPRESSED;
1772 case kNumStates:
1773 NOTREACHED();
1774 return 0;
1776 case kMenuCheck:
1777 switch (state) {
1778 case kDisabled:
1779 return extra.menu_check.is_radio ?
1780 MC_BULLETDISABLED : MC_CHECKMARKDISABLED;
1781 case kHovered:
1782 case kNormal:
1783 case kPressed:
1784 return extra.menu_check.is_radio ?
1785 MC_BULLETNORMAL : MC_CHECKMARKNORMAL;
1786 case kNumStates:
1787 NOTREACHED();
1788 return 0;
1790 case kMenuPopupArrow:
1791 case kMenuPopupGutter:
1792 case kMenuPopupSeparator:
1793 switch (state) {
1794 case kDisabled:
1795 return MBI_DISABLED;
1796 case kHovered:
1797 return MBI_HOT;
1798 case kNormal:
1799 return MBI_NORMAL;
1800 case kPressed:
1801 return MBI_PUSHED;
1802 case kNumStates:
1803 NOTREACHED();
1804 return 0;
1806 case kPushButton:
1807 switch (state) {
1808 case kDisabled:
1809 return PBS_DISABLED;
1810 case kHovered:
1811 return PBS_HOT;
1812 case kNormal:
1813 return PBS_NORMAL;
1814 case kPressed:
1815 return PBS_PRESSED;
1816 case kNumStates:
1817 NOTREACHED();
1818 return 0;
1820 case kRadio:
1821 switch (state) {
1822 case kDisabled:
1823 return RBS_UNCHECKEDDISABLED;
1824 case kHovered:
1825 return RBS_UNCHECKEDHOT;
1826 case kNormal:
1827 return RBS_UNCHECKEDNORMAL;
1828 case kPressed:
1829 return RBS_UNCHECKEDPRESSED;
1830 case kNumStates:
1831 NOTREACHED();
1832 return 0;
1834 case kScrollbarDownArrow:
1835 switch (state) {
1836 case kDisabled:
1837 return ABS_DOWNDISABLED;
1838 case kHovered:
1839 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1840 return base::win::GetVersion() < base::win::VERSION_VISTA ?
1841 ABS_DOWNHOT : ABS_DOWNHOVER;
1842 case kNormal:
1843 return ABS_DOWNNORMAL;
1844 case kPressed:
1845 return ABS_DOWNPRESSED;
1846 case kNumStates:
1847 NOTREACHED();
1848 return 0;
1850 case kScrollbarLeftArrow:
1851 switch (state) {
1852 case kDisabled:
1853 return ABS_LEFTDISABLED;
1854 case kHovered:
1855 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1856 return base::win::GetVersion() < base::win::VERSION_VISTA ?
1857 ABS_LEFTHOT : ABS_LEFTHOVER;
1858 case kNormal:
1859 return ABS_LEFTNORMAL;
1860 case kPressed:
1861 return ABS_LEFTPRESSED;
1862 case kNumStates:
1863 NOTREACHED();
1864 return 0;
1866 case kScrollbarRightArrow:
1867 switch (state) {
1868 case kDisabled:
1869 return ABS_RIGHTDISABLED;
1870 case kHovered:
1871 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1872 return base::win::GetVersion() < base::win::VERSION_VISTA ?
1873 ABS_RIGHTHOT : ABS_RIGHTHOVER;
1874 case kNormal:
1875 return ABS_RIGHTNORMAL;
1876 case kPressed:
1877 return ABS_RIGHTPRESSED;
1878 case kNumStates:
1879 NOTREACHED();
1880 return 0;
1882 break;
1883 case kScrollbarUpArrow:
1884 switch (state) {
1885 case kDisabled:
1886 return ABS_UPDISABLED;
1887 case kHovered:
1888 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1889 return base::win::GetVersion() < base::win::VERSION_VISTA ?
1890 ABS_UPHOT : ABS_UPHOVER;
1891 case kNormal:
1892 return ABS_UPNORMAL;
1893 case kPressed:
1894 return ABS_UPPRESSED;
1895 case kNumStates:
1896 NOTREACHED();
1897 return 0;
1899 break;
1900 case kScrollbarHorizontalThumb:
1901 case kScrollbarVerticalThumb:
1902 switch (state) {
1903 case kDisabled:
1904 return SCRBS_DISABLED;
1905 case kHovered:
1906 // Mimic WebKit's behaviour in ScrollbarThemeChromiumWin.cpp.
1907 return base::win::GetVersion() < base::win::VERSION_VISTA ?
1908 SCRBS_HOT : SCRBS_HOVER;
1909 case kNormal:
1910 return SCRBS_NORMAL;
1911 case kPressed:
1912 return SCRBS_PRESSED;
1913 case kNumStates:
1914 NOTREACHED();
1915 return 0;
1917 case kWindowResizeGripper:
1918 switch (state) {
1919 case kDisabled:
1920 case kHovered:
1921 case kNormal:
1922 case kPressed:
1923 return 1; // gripper has no windows state
1924 case kNumStates:
1925 NOTREACHED();
1926 return 0;
1928 case kComboboxArrow:
1929 case kInnerSpinButton:
1930 case kMenuList:
1931 case kMenuCheckBackground:
1932 case kMenuPopupBackground:
1933 case kMenuItemBackground:
1934 case kProgressBar:
1935 case kScrollbarHorizontalTrack:
1936 case kScrollbarVerticalTrack:
1937 case kScrollbarHorizontalGripper:
1938 case kScrollbarVerticalGripper:
1939 case kScrollbarCorner:
1940 case kSliderTrack:
1941 case kSliderThumb:
1942 case kTabPanelBackground:
1943 case kTextField:
1944 case kTrackbarThumb:
1945 case kTrackbarTrack:
1946 case kMaxPart:
1947 NOTREACHED();
1949 return 0;
1952 HRESULT NativeThemeWin::GetThemeInt(ThemeName theme,
1953 int part_id,
1954 int state_id,
1955 int prop_id,
1956 int *value) const {
1957 HANDLE handle = GetThemeHandle(theme);
1958 return (handle && get_theme_int_) ?
1959 get_theme_int_(handle, part_id, state_id, prop_id, value) : E_NOTIMPL;
1962 HRESULT NativeThemeWin::PaintFrameControl(HDC hdc,
1963 const gfx::Rect& rect,
1964 UINT type,
1965 UINT state,
1966 bool is_selected,
1967 State control_state) const {
1968 const int width = rect.width();
1969 const int height = rect.height();
1971 // DrawFrameControl for menu arrow/check wants a monochrome bitmap.
1972 base::win::ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL));
1974 if (mask_bitmap == NULL)
1975 return E_OUTOFMEMORY;
1977 base::win::ScopedCreateDC bitmap_dc(CreateCompatibleDC(NULL));
1978 base::win::ScopedSelectObject select_bitmap(bitmap_dc.Get(), mask_bitmap);
1979 RECT local_rect = { 0, 0, width, height };
1980 DrawFrameControl(bitmap_dc.Get(), &local_rect, type, state);
1982 // We're going to use BitBlt with a b&w mask. This results in using the dest
1983 // dc's text color for the black bits in the mask, and the dest dc's
1984 // background color for the white bits in the mask. DrawFrameControl draws the
1985 // check in black, and the background in white.
1986 int bg_color_key = COLOR_MENU;
1987 int text_color_key = COLOR_MENUTEXT;
1988 switch (control_state) {
1989 case kDisabled:
1990 bg_color_key = is_selected ? COLOR_HIGHLIGHT : COLOR_MENU;
1991 text_color_key = COLOR_GRAYTEXT;
1992 break;
1993 case kHovered:
1994 bg_color_key = COLOR_HIGHLIGHT;
1995 text_color_key = COLOR_HIGHLIGHTTEXT;
1996 break;
1997 case kNormal:
1998 break;
1999 case kPressed:
2000 case kNumStates:
2001 NOTREACHED();
2002 break;
2004 COLORREF old_bg_color = SetBkColor(hdc, GetSysColor(bg_color_key));
2005 COLORREF old_text_color = SetTextColor(hdc, GetSysColor(text_color_key));
2006 BitBlt(hdc, rect.x(), rect.y(), width, height, bitmap_dc.Get(), 0, 0,
2007 SRCCOPY);
2008 SetBkColor(hdc, old_bg_color);
2009 SetTextColor(hdc, old_text_color);
2011 return S_OK;
2014 HANDLE NativeThemeWin::GetThemeHandle(ThemeName theme_name) const {
2015 if (!open_theme_ || theme_name < 0 || theme_name >= LAST)
2016 return 0;
2018 if (theme_handles_[theme_name])
2019 return theme_handles_[theme_name];
2021 // Not found, try to load it.
2022 HANDLE handle = 0;
2023 switch (theme_name) {
2024 case BUTTON:
2025 handle = open_theme_(NULL, L"Button");
2026 break;
2027 case LIST:
2028 handle = open_theme_(NULL, L"Listview");
2029 break;
2030 case MENU:
2031 handle = open_theme_(NULL, L"Menu");
2032 break;
2033 case MENULIST:
2034 handle = open_theme_(NULL, L"Combobox");
2035 break;
2036 case SCROLLBAR:
2037 handle = open_theme_(NULL, L"Scrollbar");
2038 break;
2039 case STATUS:
2040 handle = open_theme_(NULL, L"Status");
2041 break;
2042 case TAB:
2043 handle = open_theme_(NULL, L"Tab");
2044 break;
2045 case TEXTFIELD:
2046 handle = open_theme_(NULL, L"Edit");
2047 break;
2048 case TRACKBAR:
2049 handle = open_theme_(NULL, L"Trackbar");
2050 break;
2051 case WINDOW:
2052 handle = open_theme_(NULL, L"Window");
2053 break;
2054 case PROGRESS:
2055 handle = open_theme_(NULL, L"Progress");
2056 break;
2057 case SPIN:
2058 handle = open_theme_(NULL, L"Spin");
2059 break;
2060 case LAST:
2061 NOTREACHED();
2062 break;
2064 theme_handles_[theme_name] = handle;
2065 return handle;
2068 } // namespace ui