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.
4
5 #include "ui/native_theme/native_theme_win.h"
6
7 #include <windows.h>
8 #include <uxtheme.h>
9 #include <vsstyle.h>
10 #include <vssym32.h>
11
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"
31
32 // This was removed from Winvers.h but is still used.
33 #if !defined(COLOR_MENUHIGHLIGHT)
34 #define COLOR_MENUHIGHLIGHT 29
35 #endif
36
37 namespace {
38
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);
59
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,
72 };
73
SetCheckerboardShader(SkPaint * paint,const RECT & align_rect)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 SkBitmap temp_bitmap;
86 temp_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
87 temp_bitmap.setPixels(buffer);
88 SkBitmap bitmap;
89 temp_bitmap.copyTo(&bitmap);
90
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());
101 }
102
103 // <-a->
104 // [ ***** ]
105 // ____ | |
106 // <-a-> <------b----->
107 // a: object_width
108 // b: frame_width
109 // *: animating object
110 //
111 // - the animation goes from "[" to "]" repeatedly.
112 // - the animation offset is at first "|"
113 //
ComputeAnimationProgress(int frame_width,int object_width,int pixels_per_second,double animated_seconds)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;
122 }
123
InsetRect(const RECT * rect,int size)124 RECT InsetRect(const RECT* rect, int size) {
125 gfx::Rect result(*rect);
126 result.Inset(size, size);
127 return result.ToRECT();
128 }
129
130 } // namespace
131
132 namespace ui {
133
IsThemingActive() const134 bool NativeThemeWin::IsThemingActive() const {
135 if (is_theme_active_)
136 return !!is_theme_active_();
137 return false;
138 }
139
IsUsingHighContrastTheme() const140 bool NativeThemeWin::IsUsingHighContrastTheme() const {
141 if (is_using_high_contrast_valid_)
142 return is_using_high_contrast_;
143 HIGHCONTRAST result;
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_;
150 }
151
GetThemeColor(ThemeName theme,int part_id,int state_id,int prop_id,SkColor * color) const152 HRESULT NativeThemeWin::GetThemeColor(ThemeName theme,
153 int part_id,
154 int state_id,
155 int prop_id,
156 SkColor* color) const {
157 HANDLE handle = GetThemeHandle(theme);
158 if (handle && get_theme_color_) {
159 COLORREF color_ref;
160 if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) ==
161 S_OK) {
162 *color = skia::COLORREFToSkColor(color_ref);
163 return S_OK;
164 }
165 }
166 return E_NOTIMPL;
167 }
168
GetThemeColorWithDefault(ThemeName theme,int part_id,int state_id,int prop_id,int default_sys_color) const169 SkColor NativeThemeWin::GetThemeColorWithDefault(ThemeName theme,
170 int part_id,
171 int state_id,
172 int prop_id,
173 int default_sys_color) const {
174 SkColor color;
175 if (GetThemeColor(theme, part_id, state_id, prop_id, &color) != S_OK)
176 color = color_utils::GetSysSkColor(default_sys_color);
177 return color;
178 }
179
GetThemeBorderSize(ThemeName theme) const180 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.
183 int border;
184 if (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK)
185 return gfx::Size(border, border);
186 else
187 return gfx::Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE));
188 }
189
DisableTheming() const190 void NativeThemeWin::DisableTheming() const {
191 if (!set_theme_properties_)
192 return;
193 set_theme_properties_(0);
194 }
195
CloseHandles() const196 void NativeThemeWin::CloseHandles() const {
197 if (!close_theme_)
198 return;
199
200 for (int i = 0; i < LAST; ++i) {
201 if (theme_handles_[i]) {
202 close_theme_(theme_handles_[i]);
203 theme_handles_[i] = NULL;
204 }
205 }
206 }
207
IsClassicTheme(ThemeName name) const208 bool NativeThemeWin::IsClassicTheme(ThemeName name) const {
209 if (!theme_dll_)
210 return true;
211
212 return !GetThemeHandle(name);
213 }
214
215 // static
instance()216 NativeThemeWin* NativeThemeWin::instance() {
217 CR_DEFINE_STATIC_LOCAL(NativeThemeWin, s_native_theme, ());
218 return &s_native_theme;
219 }
220
GetPartSize(Part part,State state,const ExtraParams & extra) const221 gfx::Size NativeThemeWin::GetPartSize(Part part,
222 State state,
223 const ExtraParams& extra) const {
224 gfx::Size part_size = CommonThemeGetPartSize(part, state, extra);
225 if (!part_size.IsEmpty())
226 return part_size;
227
228 // The GetThemePartSize call below returns the default size without
229 // accounting for user customization (crbug/218291).
230 switch (part) {
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);
240 if (size == 0)
241 size = 17;
242 return gfx::Size(size, size);
243 }
244 }
245
246 int part_id = GetWindowsPart(part, state, extra);
247 int state_id = GetWindowsState(part, state, extra);
248
249 SIZE size;
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);
254
255 if (FAILED(hr)) {
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
258 // if/when needed.
259 switch (part) {
260 case kCheckbox:
261 case kRadio:
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.
264 size.cx = 13;
265 size.cy = 13;
266 break;
267 default:
268 size.cx = 0;
269 size.cy = 0;
270 break;
271 }
272 }
273
274 return gfx::Size(size.cx, size.cy);
275 }
276
Paint(SkCanvas * canvas,Part part,State state,const gfx::Rect & rect,const ExtraParams & extra) const277 void NativeThemeWin::Paint(SkCanvas* canvas,
278 Part part,
279 State state,
280 const gfx::Rect& rect,
281 const ExtraParams& extra) const {
282 if (rect.IsEmpty())
283 return;
284
285 switch (part) {
286 case kComboboxArrow:
287 CommonThemePaintComboboxArrow(canvas, rect);
288 return;
289 case kMenuPopupGutter:
290 CommonThemePaintMenuGutter(canvas, rect);
291 return;
292 case kMenuPopupSeparator:
293 CommonThemePaintMenuSeparator(canvas, rect, extra.menu_separator);
294 return;
295 case kMenuPopupBackground:
296 CommonThemePaintMenuBackground(canvas, rect);
297 return;
298 case kMenuItemBackground:
299 CommonThemePaintMenuItemBackground(canvas, state, rect);
300 return;
301 }
302
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;
307 } else {
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
312 // fixed.
313 switch (part) {
314 case kScrollbarDownArrow:
315 case kScrollbarUpArrow:
316 case kScrollbarLeftArrow:
317 case kScrollbarRightArrow:
318 if (!GetThemeHandle(SCROLLBAR))
319 needs_paint_indirect = true;
320 break;
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;
328 break;
329 default:
330 break;
331 }
332 }
333
334 if (needs_paint_indirect)
335 PaintIndirect(canvas, part, state, rect, extra);
336 else
337 PaintDirect(canvas, part, state, rect, extra);
338 }
339
NativeThemeWin()340 NativeThemeWin::NativeThemeWin()
341 : theme_dll_(LoadLibrary(L"uxtheme.dll")),
342 draw_theme_(NULL),
343 draw_theme_ex_(NULL),
344 get_theme_color_(NULL),
345 get_theme_content_rect_(NULL),
346 get_theme_part_size_(NULL),
347 open_theme_(NULL),
348 close_theme_(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) {
355 if (theme_dll_) {
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"));
376 }
377 memset(theme_handles_, 0, sizeof(theme_handles_));
378
379 // Initialize the cached system colors.
380 UpdateSystemColors();
381 }
382
~NativeThemeWin()383 NativeThemeWin::~NativeThemeWin() {
384 if (theme_dll_) {
385 // todo (cpu): fix this soon. Making a call to CloseHandles() here breaks
386 // certain tests and the reliability bots.
387 // CloseHandles();
388 FreeLibrary(theme_dll_);
389 }
390 }
391
OnSysColorChange()392 void NativeThemeWin::OnSysColorChange() {
393 UpdateSystemColors();
394 is_using_high_contrast_valid_ = false;
395 NotifyObservers();
396 }
397
UpdateSystemColors()398 void NativeThemeWin::UpdateSystemColors() {
399 for (int i = 0; i < arraysize(kSystemColors); ++i) {
400 system_colors_[kSystemColors[i]] =
401 color_utils::GetSysSkColor(kSystemColors[i]);
402 }
403 }
404
PaintDirect(SkCanvas * canvas,Part part,State state,const gfx::Rect & rect,const ExtraParams & extra) const405 void NativeThemeWin::PaintDirect(SkCanvas* canvas,
406 Part part,
407 State state,
408 const gfx::Rect& rect,
409 const ExtraParams& extra) const {
410 skia::ScopedPlatformPaint scoped_platform_paint(canvas);
411 HDC hdc = scoped_platform_paint.GetPlatformSurface();
412
413 switch (part) {
414 case kCheckbox:
415 PaintCheckbox(hdc, part, state, rect, extra.button);
416 break;
417 case kRadio:
418 PaintRadioButton(hdc, part, state, rect, extra.button);
419 break;
420 case kPushButton:
421 PaintPushButton(hdc, part, state, rect, extra.button);
422 break;
423 case kMenuPopupArrow:
424 PaintMenuArrow(hdc, state, rect, extra.menu_arrow);
425 break;
426 case kMenuPopupGutter:
427 PaintMenuGutter(hdc, rect);
428 break;
429 case kMenuPopupSeparator:
430 PaintMenuSeparator(hdc, rect, extra.menu_separator);
431 break;
432 case kMenuPopupBackground:
433 PaintMenuBackground(hdc, rect);
434 break;
435 case kMenuCheck:
436 PaintMenuCheck(hdc, state, rect, extra.menu_check);
437 break;
438 case kMenuCheckBackground:
439 PaintMenuCheckBackground(hdc, state, rect);
440 break;
441 case kMenuItemBackground:
442 PaintMenuItemBackground(hdc, state, rect, extra.menu_item);
443 break;
444 case kMenuList:
445 PaintMenuList(hdc, state, rect, extra.menu_list);
446 break;
447 case kScrollbarDownArrow:
448 case kScrollbarUpArrow:
449 case kScrollbarLeftArrow:
450 case kScrollbarRightArrow:
451 PaintScrollbarArrow(hdc, part, state, rect, extra.scrollbar_arrow);
452 break;
453 case kScrollbarHorizontalTrack:
454 case kScrollbarVerticalTrack:
455 PaintScrollbarTrack(canvas, hdc, part, state, rect,
456 extra.scrollbar_track);
457 break;
458 case kScrollbarCorner:
459 canvas->drawColor(SK_ColorWHITE, SkXfermode::kSrc_Mode);
460 break;
461 case kScrollbarHorizontalThumb:
462 case kScrollbarVerticalThumb:
463 case kScrollbarHorizontalGripper:
464 case kScrollbarVerticalGripper:
465 PaintScrollbarThumb(hdc, part, state, rect, extra.scrollbar_thumb);
466 break;
467 case kInnerSpinButton:
468 PaintSpinButton(hdc, part, state, rect, extra.inner_spin);
469 break;
470 case kTrackbarThumb:
471 case kTrackbarTrack:
472 PaintTrackbar(canvas, hdc, part, state, rect, extra.trackbar);
473 break;
474 case kProgressBar:
475 PaintProgressBar(hdc, rect, extra.progress_bar);
476 break;
477 case kWindowResizeGripper:
478 PaintWindowResizeGripper(hdc, rect);
479 break;
480 case kTabPanelBackground:
481 PaintTabPanelBackground(hdc, rect);
482 break;
483 case kTextField:
484 PaintTextField(hdc, part, state, rect, extra.text_field);
485 break;
486
487 case kSliderTrack:
488 case kSliderThumb:
489 default:
490 // While transitioning NativeThemeWin to the single Paint() entry point,
491 // unsupported parts will DCHECK here.
492 NOTREACHED();
493 }
494 }
495
GetSystemColor(ColorId color_id) const496 SkColor NativeThemeWin::GetSystemColor(ColorId color_id) const {
497 SkColor color;
498 if (CommonThemeGetSystemColor(color_id, &color))
499 return color;
500
501 switch (color_id) {
502 // Windows
503 case kColorId_WindowBackground:
504 return system_colors_[COLOR_WINDOW];
505
506 // Dialogs
507 case kColorId_DialogBackground:
508 if (gfx::IsInvertedColorScheme())
509 return color_utils::InvertColor(kDialogBackgroundColor);
510 return kDialogBackgroundColor;
511
512 // FocusableBorder
513 case kColorId_FocusedBorderColor:
514 return kFocusedBorderColor;
515 case kColorId_UnfocusedBorderColor:
516 return kUnfocusedBorderColor;
517
518 // Button
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;
531
532 // MenuItem
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;
543
544 // Label
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];
551
552 // Textfield
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];
565
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];
583
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];
600
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);
644
645 default:
646 NOTREACHED();
647 break;
648 }
649 return kInvalidColorIdColor;
650 }
651
PaintIndirect(SkCanvas * canvas,Part part,State state,const gfx::Rect & rect,const ExtraParams & extra) const652 void NativeThemeWin::PaintIndirect(SkCanvas* canvas,
653 Part part,
654 State state,
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.
661
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));
666 DCHECK(device);
667 if (!device)
668 return;
669 SkCanvas offscreen_canvas(device.get());
670 DCHECK(skia::SupportsPlatformPaint(&offscreen_canvas));
671
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.
675 //
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);
681
682 // Offset destination rects to have origin (0,0).
683 gfx::Rect adjusted_rect(rect.size());
684 ExtraParams adjusted_extra(extra);
685 switch (part) {
686 case kProgressBar:
687 adjusted_extra.progress_bar.value_rect_x = 0;
688 adjusted_extra.progress_bar.value_rect_y = 0;
689 break;
690 case kScrollbarHorizontalTrack:
691 case kScrollbarVerticalTrack:
692 adjusted_extra.scrollbar_track.track_x = 0;
693 adjusted_extra.scrollbar_track.track_y = 0;
694 break;
695 default: break;
696 }
697 // Draw the theme controls using existing HDC-drawing code.
698 PaintDirect(&offscreen_canvas,
699 part,
700 state,
701 adjusted_rect,
702 adjusted_extra);
703
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);
708 SkBitmap bitmap;
709 hdc_bitmap.copyTo(&bitmap, kPMColor_SkColorType);
710
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]));
725 }
726 }
727
728 // Draw the offscreen bitmap to the destination canvas.
729 canvas->drawBitmap(bitmap, rect.x(), rect.y());
730 }
731
GetThemePartSize(ThemeName theme_name,HDC hdc,int part_id,int state_id,RECT * rect,int ts,SIZE * size) const732 HRESULT NativeThemeWin::GetThemePartSize(ThemeName theme_name,
733 HDC hdc,
734 int part_id,
735 int state_id,
736 RECT* rect,
737 int ts,
738 SIZE* size) const {
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);
742
743 return E_NOTIMPL;
744 }
745
PaintButton(HDC hdc,State state,const ButtonExtraParams & extra,int part_id,int state_id,RECT * rect) const746 HRESULT NativeThemeWin::PaintButton(HDC hdc,
747 State state,
748 const ButtonExtraParams& extra,
749 int part_id,
750 int state_id,
751 RECT* rect) const {
752 HANDLE handle = GetThemeHandle(BUTTON);
753 if (handle && draw_theme_)
754 return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
755
756 // Adjust classic_state based on part, state, and extras.
757 int classic_state = extra.classic_state;
758 switch (part_id) {
759 case BP_CHECKBOX:
760 classic_state |= DFCS_BUTTONCHECK;
761 break;
762 case BP_RADIOBUTTON:
763 classic_state |= DFCS_BUTTONRADIO;
764 break;
765 case BP_PUSHBUTTON:
766 classic_state |= DFCS_BUTTONPUSH;
767 break;
768 default:
769 NOTREACHED() << "Unknown part_id: " << part_id;
770 break;
771 }
772
773 switch (state) {
774 case kDisabled:
775 classic_state |= DFCS_INACTIVE;
776 break;
777 case kPressed:
778 classic_state |= DFCS_PUSHED;
779 break;
780 case kNormal:
781 case kHovered:
782 break;
783 default:
784 NOTREACHED() << "Unknown state: " << state;
785 break;
786 }
787
788 if (extra.checked)
789 classic_state |= DFCS_CHECKED;
790
791 // Draw it manually.
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);
799 if (brush) {
800 FrameRect(hdc, rect, brush);
801 InflateRect(rect, -1, -1);
802 }
803 }
804 DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state);
805
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);
813 } else {
814 InflateRect(rect, -GetSystemMetrics(SM_CXEDGE),
815 -GetSystemMetrics(SM_CYEDGE));
816 }
817 DrawFocusRect(hdc, rect);
818 }
819
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));
829 }
830
831 return S_OK;
832 }
833
PaintMenuSeparator(HDC hdc,const gfx::Rect & rect,const MenuSeparatorExtraParams & extra) const834 HRESULT NativeThemeWin::PaintMenuSeparator(
835 HDC hdc,
836 const gfx::Rect& rect,
837 const MenuSeparatorExtraParams& extra) const {
838 RECT rect_win = rect.ToRECT();
839
840 HANDLE handle = GetThemeHandle(MENU);
841 if (handle && draw_theme_) {
842 // Delta is needed for non-classic to move separator up slightly.
843 --rect_win.top;
844 --rect_win.bottom;
845 return draw_theme_(handle, hdc, MENU_POPUPSEPARATOR, MPI_NORMAL, &rect_win,
846 NULL);
847 }
848
849 DrawEdge(hdc, &rect_win, EDGE_ETCHED, BF_TOP);
850 return S_OK;
851 }
852
PaintMenuGutter(HDC hdc,const gfx::Rect & rect) const853 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,
859 NULL);
860 return E_NOTIMPL;
861 }
862
PaintMenuArrow(HDC hdc,State state,const gfx::Rect & rect,const MenuArrowExtraParams & extra) const863 HRESULT NativeThemeWin::PaintMenuArrow(HDC hdc,
864 State state,
865 const gfx::Rect& rect,
866 const MenuArrowExtraParams& extra)
867 const {
868 int state_id = MSM_NORMAL;
869 if (state == kDisabled)
870 state_id = MSM_DISABLED;
871
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,
877 NULL);
878 } else {
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.
883 gfx::Rect r(rect);
884 base::win::ScopedCreateDC mem_dc(CreateCompatibleDC(hdc));
885 base::win::ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(),
886 r.height()));
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);
892 // Draw the arrow.
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);
899 return result;
900 }
901 }
902
903 // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
904 // left pointing arrow. This makes the following 'if' statement slightly
905 // counterintuitive.
906 UINT pfc_state;
907 if (extra.pointing_right)
908 pfc_state = DFCS_MENUARROW;
909 else
910 pfc_state = DFCS_MENUARROWRIGHT;
911 return PaintFrameControl(hdc, rect, DFC_MENU, pfc_state, extra.is_selected,
912 state);
913 }
914
PaintMenuBackground(HDC hdc,const gfx::Rect & rect) const915 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,
921 &rect_win, NULL);
922 FrameRect(hdc, &rect_win, GetSysColorBrush(COLOR_3DSHADOW));
923 return result;
924 }
925
926 FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_MENU));
927 DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT);
928 return S_OK;
929 }
930
PaintMenuCheck(HDC hdc,State state,const gfx::Rect & rect,const MenuCheckExtraParams & extra) const931 HRESULT NativeThemeWin::PaintMenuCheck(
932 HDC hdc,
933 State state,
934 const gfx::Rect& rect,
935 const MenuCheckExtraParams& extra) const {
936 HANDLE handle = GetThemeHandle(MENU);
937 int state_id;
938 if (extra.is_radio) {
939 state_id = state == kDisabled ? MC_BULLETDISABLED : MC_BULLETNORMAL;
940 } else {
941 state_id = state == kDisabled ? MC_CHECKMARKDISABLED : MC_CHECKMARKNORMAL;
942 }
943
944 RECT rect_win = rect.ToRECT();
945 if (handle && draw_theme_)
946 return draw_theme_(handle, hdc, MENU_POPUPCHECK, state_id, &rect_win, NULL);
947
948 return PaintFrameControl(hdc, rect, DFC_MENU,
949 extra.is_radio ? DFCS_MENUBULLET : DFCS_MENUCHECK,
950 extra.is_selected, state);
951 }
952
PaintMenuCheckBackground(HDC hdc,State state,const gfx::Rect & rect) const953 HRESULT NativeThemeWin::PaintMenuCheckBackground(HDC hdc,
954 State state,
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,
961 &rect_win, NULL);
962 // Nothing to do for background.
963 return S_OK;
964 }
965
PaintMenuItemBackground(HDC hdc,State state,const gfx::Rect & rect,const MenuItemExtraParams & extra) const966 HRESULT NativeThemeWin::PaintMenuItemBackground(
967 HDC hdc,
968 State state,
969 const gfx::Rect& rect,
970 const MenuItemExtraParams& extra) const {
971 HANDLE handle = GetThemeHandle(MENU);
972 RECT rect_win = rect.ToRECT();
973 int state_id;
974 switch (state) {
975 case kNormal:
976 state_id = MPI_NORMAL;
977 break;
978 case kDisabled:
979 state_id = extra.is_selected ? MPI_DISABLEDHOT : MPI_DISABLED;
980 break;
981 case kHovered:
982 state_id = MPI_HOT;
983 break;
984 default:
985 NOTREACHED() << "Invalid state " << state;
986 break;
987 }
988
989 if (handle && draw_theme_)
990 return draw_theme_(handle, hdc, MENU_POPUPITEM, state_id, &rect_win, NULL);
991
992 if (extra.is_selected)
993 FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_HIGHLIGHT));
994 return S_OK;
995 }
996
PaintPushButton(HDC hdc,Part part,State state,const gfx::Rect & rect,const ButtonExtraParams & extra) const997 HRESULT NativeThemeWin::PaintPushButton(HDC hdc,
998 Part part,
999 State state,
1000 const gfx::Rect& rect,
1001 const ButtonExtraParams& extra) const {
1002 int state_id;
1003 switch (state) {
1004 case kDisabled:
1005 state_id = PBS_DISABLED;
1006 break;
1007 case kHovered:
1008 state_id = PBS_HOT;
1009 break;
1010 case kNormal:
1011 state_id = extra.is_default ? PBS_DEFAULTED : PBS_NORMAL;
1012 break;
1013 case kPressed:
1014 state_id = PBS_PRESSED;
1015 break;
1016 default:
1017 NOTREACHED() << "Invalid state: " << state;
1018 break;
1019 }
1020
1021 RECT rect_win = rect.ToRECT();
1022 return PaintButton(hdc, state, extra, BP_PUSHBUTTON, state_id, &rect_win);
1023 }
1024
PaintRadioButton(HDC hdc,Part part,State state,const gfx::Rect & rect,const ButtonExtraParams & extra) const1025 HRESULT NativeThemeWin::PaintRadioButton(HDC hdc,
1026 Part part,
1027 State state,
1028 const gfx::Rect& rect,
1029 const ButtonExtraParams& extra) const {
1030 int state_id;
1031 switch (state) {
1032 case kDisabled:
1033 state_id = extra.checked ? RBS_CHECKEDDISABLED : RBS_UNCHECKEDDISABLED;
1034 break;
1035 case kHovered:
1036 state_id = extra.checked ? RBS_CHECKEDHOT : RBS_UNCHECKEDHOT;
1037 break;
1038 case kNormal:
1039 state_id = extra.checked ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL;
1040 break;
1041 case kPressed:
1042 state_id = extra.checked ? RBS_CHECKEDPRESSED : RBS_UNCHECKEDPRESSED;
1043 break;
1044 default:
1045 NOTREACHED() << "Invalid state: " << state;
1046 break;
1047 }
1048
1049 RECT rect_win = rect.ToRECT();
1050 return PaintButton(hdc, state, extra, BP_RADIOBUTTON, state_id, &rect_win);
1051 }
1052
PaintCheckbox(HDC hdc,Part part,State state,const gfx::Rect & rect,const ButtonExtraParams & extra) const1053 HRESULT NativeThemeWin::PaintCheckbox(HDC hdc,
1054 Part part,
1055 State state,
1056 const gfx::Rect& rect,
1057 const ButtonExtraParams& extra) const {
1058 int state_id;
1059 switch (state) {
1060 case kDisabled:
1061 state_id = extra.checked ? CBS_CHECKEDDISABLED :
1062 extra.indeterminate ? CBS_MIXEDDISABLED :
1063 CBS_UNCHECKEDDISABLED;
1064 break;
1065 case kHovered:
1066 state_id = extra.checked ? CBS_CHECKEDHOT :
1067 extra.indeterminate ? CBS_MIXEDHOT :
1068 CBS_UNCHECKEDHOT;
1069 break;
1070 case kNormal:
1071 state_id = extra.checked ? CBS_CHECKEDNORMAL :
1072 extra.indeterminate ? CBS_MIXEDNORMAL :
1073 CBS_UNCHECKEDNORMAL;
1074 break;
1075 case kPressed:
1076 state_id = extra.checked ? CBS_CHECKEDPRESSED :
1077 extra.indeterminate ? CBS_MIXEDPRESSED :
1078 CBS_UNCHECKEDPRESSED;
1079 break;
1080 default:
1081 NOTREACHED() << "Invalid state: " << state;
1082 break;
1083 }
1084
1085 RECT rect_win = rect.ToRECT();
1086 return PaintButton(hdc, state, extra, BP_CHECKBOX, state_id, &rect_win);
1087 }
1088
PaintMenuList(HDC hdc,State state,const gfx::Rect & rect,const MenuListExtraParams & extra) const1089 HRESULT NativeThemeWin::PaintMenuList(HDC hdc,
1090 State state,
1091 const gfx::Rect& rect,
1092 const MenuListExtraParams& extra) const {
1093 HANDLE handle = GetThemeHandle(MENULIST);
1094 RECT rect_win = rect.ToRECT();
1095 int state_id;
1096 switch (state) {
1097 case kNormal:
1098 state_id = CBXS_NORMAL;
1099 break;
1100 case kDisabled:
1101 state_id = CBXS_DISABLED;
1102 break;
1103 case kHovered:
1104 state_id = CBXS_HOT;
1105 break;
1106 case kPressed:
1107 state_id = CBXS_PRESSED;
1108 break;
1109 default:
1110 NOTREACHED() << "Invalid state " << state;
1111 break;
1112 }
1113
1114 if (handle && draw_theme_)
1115 return draw_theme_(handle, hdc, CP_DROPDOWNBUTTON, state_id, &rect_win,
1116 NULL);
1117
1118 // Draw it manually.
1119 DrawFrameControl(hdc, &rect_win, DFC_SCROLL,
1120 DFCS_SCROLLCOMBOBOX | extra.classic_state);
1121 return S_OK;
1122 }
1123
PaintScrollbarArrow(HDC hdc,Part part,State state,const gfx::Rect & rect,const ScrollbarArrowExtraParams & extra) const1124 HRESULT NativeThemeWin::PaintScrollbarArrow(
1125 HDC hdc,
1126 Part part,
1127 State state,
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
1135 };
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];
1142
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
1145 // in "hover" mode.
1146 if (state == kHovered && extra.is_hovering) {
1147 switch (part) {
1148 case kScrollbarDownArrow:
1149 state_id = ABS_DOWNHOVER;
1150 break;
1151 case kScrollbarLeftArrow:
1152 state_id = ABS_LEFTHOVER;
1153 break;
1154 case kScrollbarRightArrow:
1155 state_id = ABS_RIGHTHOVER;
1156 break;
1157 case kScrollbarUpArrow:
1158 state_id = ABS_UPHOVER;
1159 break;
1160 default:
1161 NOTREACHED() << "Invalid part: " << part;
1162 break;
1163 }
1164 }
1165 return PaintScaledTheme(handle, hdc, SBP_ARROWBTN, state_id, rect);
1166 }
1167
1168 int classic_state = DFCS_SCROLLDOWN;
1169 switch (part) {
1170 case kScrollbarDownArrow:
1171 classic_state = DFCS_SCROLLDOWN;
1172 break;
1173 case kScrollbarLeftArrow:
1174 classic_state = DFCS_SCROLLLEFT;
1175 break;
1176 case kScrollbarRightArrow:
1177 classic_state = DFCS_SCROLLRIGHT;
1178 break;
1179 case kScrollbarUpArrow:
1180 classic_state = DFCS_SCROLLUP;
1181 break;
1182 default:
1183 NOTREACHED() << "Invalid part: " << part;
1184 break;
1185 }
1186 switch (state) {
1187 case kDisabled:
1188 classic_state |= DFCS_INACTIVE;
1189 break;
1190 case kHovered:
1191 classic_state |= DFCS_HOT;
1192 break;
1193 case kNormal:
1194 break;
1195 case kPressed:
1196 classic_state |= DFCS_PUSHED;
1197 break;
1198 default:
1199 NOTREACHED() << "Invalid state: " << state;
1200 break;
1201 }
1202 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, classic_state);
1203 return S_OK;
1204 }
1205
PaintScrollbarThumb(HDC hdc,Part part,State state,const gfx::Rect & rect,const ScrollbarThumbExtraParams & extra) const1206 HRESULT NativeThemeWin::PaintScrollbarThumb(
1207 HDC hdc,
1208 Part part,
1209 State state,
1210 const gfx::Rect& rect,
1211 const ScrollbarThumbExtraParams& extra) const {
1212 HANDLE handle = GetThemeHandle(SCROLLBAR);
1213 RECT rect_win = rect.ToRECT();
1214 int part_id;
1215 int state_id;
1216
1217 switch (part) {
1218 case NativeTheme::kScrollbarHorizontalThumb:
1219 part_id = SBP_THUMBBTNHORZ;
1220 break;
1221 case NativeTheme::kScrollbarVerticalThumb:
1222 part_id = SBP_THUMBBTNVERT;
1223 break;
1224 case NativeTheme::kScrollbarHorizontalGripper:
1225 part_id = SBP_GRIPPERHORZ;
1226 break;
1227 case NativeTheme::kScrollbarVerticalGripper:
1228 part_id = SBP_GRIPPERVERT;
1229 break;
1230 default:
1231 NOTREACHED() << "Invalid part: " << part;
1232 break;
1233 }
1234
1235 switch (state) {
1236 case kDisabled:
1237 state_id = SCRBS_DISABLED;
1238 break;
1239 case kHovered:
1240 state_id = extra.is_hovering ? SCRBS_HOVER : SCRBS_HOT;
1241 break;
1242 case kNormal:
1243 state_id = SCRBS_NORMAL;
1244 break;
1245 case kPressed:
1246 state_id = SCRBS_PRESSED;
1247 break;
1248 default:
1249 NOTREACHED() << "Invalid state: " << state;
1250 break;
1251 }
1252
1253 if (handle && draw_theme_)
1254 return PaintScaledTheme(handle, hdc, part_id, state_id, rect);
1255
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.
1260 return S_OK;
1261 }
1262
PaintScrollbarTrack(SkCanvas * canvas,HDC hdc,Part part,State state,const gfx::Rect & rect,const ScrollbarTrackExtraParams & extra) const1263 HRESULT NativeThemeWin::PaintScrollbarTrack(
1264 SkCanvas* canvas,
1265 HDC hdc,
1266 Part part,
1267 State state,
1268 const gfx::Rect& rect,
1269 const ScrollbarTrackExtraParams& extra) const {
1270 HANDLE handle = GetThemeHandle(SCROLLBAR);
1271 RECT rect_win = rect.ToRECT();
1272 int part_id;
1273 int state_id;
1274
1275 switch (part) {
1276 case NativeTheme::kScrollbarHorizontalTrack:
1277 part_id = extra.is_upper ? SBP_UPPERTRACKHORZ : SBP_LOWERTRACKHORZ;
1278 break;
1279 case NativeTheme::kScrollbarVerticalTrack:
1280 part_id = extra.is_upper ? SBP_UPPERTRACKVERT : SBP_LOWERTRACKVERT;
1281 break;
1282 default:
1283 NOTREACHED() << "Invalid part: " << part;
1284 break;
1285 }
1286
1287 switch (state) {
1288 case kDisabled:
1289 state_id = SCRBS_DISABLED;
1290 break;
1291 case kHovered:
1292 state_id = SCRBS_HOVER;
1293 break;
1294 case kNormal:
1295 state_id = SCRBS_NORMAL;
1296 break;
1297 case kPressed:
1298 state_id = SCRBS_PRESSED;
1299 break;
1300 default:
1301 NOTREACHED() << "Invalid state: " << state;
1302 break;
1303 }
1304
1305 if (handle && draw_theme_)
1306 return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
1307
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));
1312 } else {
1313 SkPaint paint;
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);
1318 }
1319 if (extra.classic_state & DFCS_PUSHED)
1320 InvertRect(hdc, &rect_win);
1321 return S_OK;
1322 }
1323
PaintSpinButton(HDC hdc,Part part,State state,const gfx::Rect & rect,const InnerSpinButtonExtraParams & extra) const1324 HRESULT NativeThemeWin::PaintSpinButton(
1325 HDC hdc,
1326 Part part,
1327 State state,
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;
1333 int state_id;
1334 switch (state) {
1335 case kDisabled:
1336 state_id = extra.spin_up ? UPS_DISABLED : DNS_DISABLED;
1337 break;
1338 case kHovered:
1339 state_id = extra.spin_up ? UPS_HOT : DNS_HOT;
1340 break;
1341 case kNormal:
1342 state_id = extra.spin_up ? UPS_NORMAL : DNS_NORMAL;
1343 break;
1344 case kPressed:
1345 state_id = extra.spin_up ? UPS_PRESSED : DNS_PRESSED;
1346 break;
1347 default:
1348 NOTREACHED() << "Invalid state " << state;
1349 break;
1350 }
1351
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);
1355 return S_OK;
1356 }
1357
PaintTrackbar(SkCanvas * canvas,HDC hdc,Part part,State state,const gfx::Rect & rect,const TrackbarExtraParams & extra) const1358 HRESULT NativeThemeWin::PaintTrackbar(
1359 SkCanvas* canvas,
1360 HDC hdc,
1361 Part part,
1362 State state,
1363 const gfx::Rect& rect,
1364 const TrackbarExtraParams& extra) const {
1365 int part_id = part == kTrackbarTrack ? TKP_TRACK : TKP_THUMBBOTTOM;
1366 if (extra.vertical)
1367 part_id = part == kTrackbarTrack ? TKP_TRACKVERT : TKP_THUMBVERT;
1368
1369 int state_id = 0;
1370 switch (state) {
1371 case kDisabled:
1372 state_id = TUS_DISABLED;
1373 break;
1374 case kHovered:
1375 state_id = TUS_HOT;
1376 break;
1377 case kNormal:
1378 state_id = TUS_NORMAL;
1379 break;
1380 case kPressed:
1381 state_id = TUS_PRESSED;
1382 break;
1383 default:
1384 NOTREACHED() << "Invalid state " << state;
1385 break;
1386 }
1387
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) {
1395 channel_rect.top +=
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|.
1403
1404 HANDLE handle = GetThemeHandle(TRACKBAR);
1405 if (handle && draw_theme_)
1406 return draw_theme_(handle, hdc, part_id, state_id, &channel_rect, NULL);
1407
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);
1413 } else {
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);
1421
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);
1431
1432 // If the button is pressed, draw hatching.
1433 if (extra.classic_state & DFCS_PUSHED) {
1434 SkPaint paint;
1435 SetCheckerboardShader(&paint, rect_win);
1436
1437 // Fill all three pieces with the pattern.
1438 canvas->drawIRect(skia::RECTToSkIRect(top_section), paint);
1439
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);
1449
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);
1460 }
1461 }
1462 return S_OK;
1463 }
1464
PaintProgressBar(HDC hdc,const gfx::Rect & rect,const ProgressBarExtraParams & extra) const1465 HRESULT NativeThemeWin::PaintProgressBar(
1466 HDC hdc,
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;
1479
1480 RECT bar_rect = rect.ToRECT();
1481 RECT value_rect = gfx::Rect(extra.value_rect_x,
1482 extra.value_rect_y,
1483 extra.value_rect_width,
1484 extra.value_rect_height).ToRECT();
1485
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);
1490
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
1498 // is asymmetric.
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;
1503
1504 if (pre_vista) {
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);
1511 } else {
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);
1518
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);
1527 }
1528 } else {
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,
1536 overlay_width,
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;
1542 if (pre_vista) {
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);
1546 } else {
1547 draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &bar_rect);
1548 }
1549 }
1550
1551 return S_OK;
1552 }
1553
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);
1559 return S_OK;
1560 }
1561
PaintWindowResizeGripper(HDC hdc,const gfx::Rect & rect) const1562 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);
1572 }
1573
1574 // Draw a windows classic scrollbar gripper.
1575 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
1576 return S_OK;
1577 }
1578
PaintTabPanelBackground(HDC hdc,const gfx::Rect & rect) const1579 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);
1585
1586 // Classic just renders a flat color background.
1587 FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
1588 return S_OK;
1589 }
1590
PaintTextField(HDC hdc,Part part,State state,const gfx::Rect & rect,const TextFieldExtraParams & extra) const1591 HRESULT NativeThemeWin::PaintTextField(
1592 HDC hdc,
1593 Part part,
1594 State state,
1595 const gfx::Rect& rect,
1596 const TextFieldExtraParams& extra) const {
1597 int part_id = EP_EDITTEXT;
1598 int state_id = ETS_NORMAL;
1599 switch (state) {
1600 case kNormal:
1601 if (extra.is_read_only) {
1602 state_id = ETS_READONLY;
1603 } else if (extra.is_focused) {
1604 state_id = ETS_FOCUSED;
1605 } else {
1606 state_id = ETS_NORMAL;
1607 }
1608 break;
1609 case kHovered:
1610 state_id = ETS_HOT;
1611 break;
1612 case kPressed:
1613 state_id = ETS_SELECTED;
1614 break;
1615 case kDisabled:
1616 state_id = ETS_DISABLED;
1617 break;
1618 default:
1619 NOTREACHED() << "Invalid state: " << state;
1620 break;
1621 }
1622
1623 RECT rect_win = rect.ToRECT();
1624 return PaintTextField(hdc, part_id, state_id, extra.classic_state,
1625 &rect_win,
1626 skia::SkColorToCOLORREF(extra.background_color),
1627 extra.fill_content_area, extra.draw_edges);
1628 }
1629
PaintTextField(HDC hdc,int part_id,int state_id,int classic_state,RECT * rect,COLORREF color,bool fill_content_area,bool draw_edges) const1630 HRESULT NativeThemeWin::PaintTextField(HDC hdc,
1631 int part_id,
1632 int state_id,
1633 int classic_state,
1634 RECT* rect,
1635 COLORREF color,
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.
1640
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);
1646 HRESULT hr;
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 = {
1652 sizeof(DTBGOPTS),
1653 DTBG_OMITBORDER,
1654 { 0, 0, 0, 0 }
1655 };
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);
1658 } else {
1659 hr = draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
1660 }
1661
1662 // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
1663 if (fill_content_area && get_theme_content_rect_) {
1664 RECT content_rect;
1665 hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect,
1666 &content_rect);
1667 FillRect(hdc, &content_rect, bg_brush);
1668 }
1669 } else {
1670 // Draw it manually.
1671 if (draw_edges)
1672 DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1673
1674 if (fill_content_area) {
1675 FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ?
1676 reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1) : bg_brush);
1677 }
1678 hr = S_OK;
1679 }
1680 DeleteObject(bg_brush);
1681 return hr;
1682 }
1683
PaintScaledTheme(HANDLE theme,HDC hdc,int part_id,int state_id,const gfx::Rect & rect) const1684 HRESULT NativeThemeWin::PaintScaledTheme(HANDLE theme,
1685 HDC hdc,
1686 int part_id,
1687 int state_id,
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,
1704 NULL);
1705 SetWorldTransform(hdc, &save_transform);
1706 return result;
1707 }
1708 }
1709 RECT bounds = rect.ToRECT();
1710 return draw_theme_(theme, hdc, part_id, state_id, &bounds, NULL);
1711 }
1712
1713 // static
GetThemeName(Part part)1714 NativeThemeWin::ThemeName NativeThemeWin::GetThemeName(Part part) {
1715 ThemeName name;
1716 switch (part) {
1717 case kCheckbox:
1718 case kRadio:
1719 case kPushButton:
1720 name = BUTTON;
1721 break;
1722 case kInnerSpinButton:
1723 name = SPIN;
1724 break;
1725 case kMenuCheck:
1726 case kMenuPopupGutter:
1727 case kMenuList:
1728 case kMenuPopupArrow:
1729 case kMenuPopupSeparator:
1730 name = MENU;
1731 break;
1732 case kProgressBar:
1733 name = PROGRESS;
1734 break;
1735 case kScrollbarDownArrow:
1736 case kScrollbarLeftArrow:
1737 case kScrollbarRightArrow:
1738 case kScrollbarUpArrow:
1739 case kScrollbarHorizontalThumb:
1740 case kScrollbarVerticalThumb:
1741 case kScrollbarHorizontalTrack:
1742 case kScrollbarVerticalTrack:
1743 name = SCROLLBAR;
1744 break;
1745 case kSliderTrack:
1746 case kSliderThumb:
1747 name = TRACKBAR;
1748 break;
1749 case kTextField:
1750 name = TEXTFIELD;
1751 break;
1752 case kWindowResizeGripper:
1753 name = STATUS;
1754 break;
1755 default:
1756 NOTREACHED() << "Invalid part: " << part;
1757 break;
1758 }
1759 return name;
1760 }
1761
1762 // static
GetWindowsPart(Part part,State state,const ExtraParams & extra)1763 int NativeThemeWin::GetWindowsPart(Part part,
1764 State state,
1765 const ExtraParams& extra) {
1766 int part_id;
1767 switch (part) {
1768 case kCheckbox:
1769 part_id = BP_CHECKBOX;
1770 break;
1771 case kMenuCheck:
1772 part_id = MENU_POPUPCHECK;
1773 break;
1774 case kMenuPopupArrow:
1775 part_id = MENU_POPUPSUBMENU;
1776 break;
1777 case kMenuPopupGutter:
1778 part_id = MENU_POPUPGUTTER;
1779 break;
1780 case kMenuPopupSeparator:
1781 part_id = MENU_POPUPSEPARATOR;
1782 break;
1783 case kPushButton:
1784 part_id = BP_PUSHBUTTON;
1785 break;
1786 case kRadio:
1787 part_id = BP_RADIOBUTTON;
1788 break;
1789 case kWindowResizeGripper:
1790 part_id = SP_GRIPPER;
1791 break;
1792 case kScrollbarDownArrow:
1793 case kScrollbarLeftArrow:
1794 case kScrollbarRightArrow:
1795 case kScrollbarUpArrow:
1796 part_id = SBP_ARROWBTN;
1797 break;
1798 case kScrollbarHorizontalThumb:
1799 part_id = SBP_THUMBBTNHORZ;
1800 break;
1801 case kScrollbarVerticalThumb:
1802 part_id = SBP_THUMBBTNVERT;
1803 break;
1804 default:
1805 NOTREACHED() << "Invalid part: " << part;
1806 break;
1807 }
1808 return part_id;
1809 }
1810
GetWindowsState(Part part,State state,const ExtraParams & extra)1811 int NativeThemeWin::GetWindowsState(Part part,
1812 State state,
1813 const ExtraParams& extra) {
1814 int state_id;
1815 switch (part) {
1816 case kCheckbox:
1817 switch (state) {
1818 case kNormal:
1819 state_id = CBS_UNCHECKEDNORMAL;
1820 break;
1821 case kHovered:
1822 state_id = CBS_UNCHECKEDHOT;
1823 break;
1824 case kPressed:
1825 state_id = CBS_UNCHECKEDPRESSED;
1826 break;
1827 case kDisabled:
1828 state_id = CBS_UNCHECKEDDISABLED;
1829 break;
1830 default:
1831 NOTREACHED() << "Invalid state: " << state;
1832 break;
1833 }
1834 break;
1835 case kMenuCheck:
1836 switch (state) {
1837 case kNormal:
1838 case kHovered:
1839 case kPressed:
1840 state_id = extra.menu_check.is_radio ? MC_BULLETNORMAL
1841 : MC_CHECKMARKNORMAL;
1842 break;
1843 case kDisabled:
1844 state_id = extra.menu_check.is_radio ? MC_BULLETDISABLED
1845 : MC_CHECKMARKDISABLED;
1846 break;
1847 default:
1848 NOTREACHED() << "Invalid state: " << state;
1849 break;
1850 }
1851 break;
1852 case kMenuPopupArrow:
1853 case kMenuPopupGutter:
1854 case kMenuPopupSeparator:
1855 switch (state) {
1856 case kNormal:
1857 state_id = MBI_NORMAL;
1858 break;
1859 case kHovered:
1860 state_id = MBI_HOT;
1861 break;
1862 case kPressed:
1863 state_id = MBI_PUSHED;
1864 break;
1865 case kDisabled:
1866 state_id = MBI_DISABLED;
1867 break;
1868 default:
1869 NOTREACHED() << "Invalid state: " << state;
1870 break;
1871 }
1872 break;
1873 case kPushButton:
1874 switch (state) {
1875 case kNormal:
1876 state_id = PBS_NORMAL;
1877 break;
1878 case kHovered:
1879 state_id = PBS_HOT;
1880 break;
1881 case kPressed:
1882 state_id = PBS_PRESSED;
1883 break;
1884 case kDisabled:
1885 state_id = PBS_DISABLED;
1886 break;
1887 default:
1888 NOTREACHED() << "Invalid state: " << state;
1889 break;
1890 }
1891 break;
1892 case kRadio:
1893 switch (state) {
1894 case kNormal:
1895 state_id = RBS_UNCHECKEDNORMAL;
1896 break;
1897 case kHovered:
1898 state_id = RBS_UNCHECKEDHOT;
1899 break;
1900 case kPressed:
1901 state_id = RBS_UNCHECKEDPRESSED;
1902 break;
1903 case kDisabled:
1904 state_id = RBS_UNCHECKEDDISABLED;
1905 break;
1906 default:
1907 NOTREACHED() << "Invalid state: " << state;
1908 break;
1909 }
1910 break;
1911 case kWindowResizeGripper:
1912 switch (state) {
1913 case kNormal:
1914 case kHovered:
1915 case kPressed:
1916 case kDisabled:
1917 state_id = 1; // gripper has no windows state
1918 break;
1919 default:
1920 NOTREACHED() << "Invalid state: " << state;
1921 break;
1922 }
1923 break;
1924 case kScrollbarDownArrow:
1925 switch (state) {
1926 case kNormal:
1927 state_id = ABS_DOWNNORMAL;
1928 break;
1929 case kHovered:
1930 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1931 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1932 ABS_DOWNHOT : ABS_DOWNHOVER;
1933 break;
1934 case kPressed:
1935 state_id = ABS_DOWNPRESSED;
1936 break;
1937 case kDisabled:
1938 state_id = ABS_DOWNDISABLED;
1939 break;
1940 default:
1941 NOTREACHED() << "Invalid state: " << state;
1942 break;
1943 }
1944 break;
1945 case kScrollbarLeftArrow:
1946 switch (state) {
1947 case kNormal:
1948 state_id = ABS_LEFTNORMAL;
1949 break;
1950 case kHovered:
1951 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1952 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1953 ABS_LEFTHOT : ABS_LEFTHOVER;
1954 break;
1955 case kPressed:
1956 state_id = ABS_LEFTPRESSED;
1957 break;
1958 case kDisabled:
1959 state_id = ABS_LEFTDISABLED;
1960 break;
1961 default:
1962 NOTREACHED() << "Invalid state: " << state;
1963 break;
1964 }
1965 break;
1966 case kScrollbarRightArrow:
1967 switch (state) {
1968 case kNormal:
1969 state_id = ABS_RIGHTNORMAL;
1970 break;
1971 case kHovered:
1972 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1973 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1974 ABS_RIGHTHOT : ABS_RIGHTHOVER;
1975 break;
1976 case kPressed:
1977 state_id = ABS_RIGHTPRESSED;
1978 break;
1979 case kDisabled:
1980 state_id = ABS_RIGHTDISABLED;
1981 break;
1982 default:
1983 NOTREACHED() << "Invalid state: " << state;
1984 break;
1985 }
1986 break;
1987 case kScrollbarUpArrow:
1988 switch (state) {
1989 case kNormal:
1990 state_id = ABS_UPNORMAL;
1991 break;
1992 case kHovered:
1993 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1994 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1995 ABS_UPHOT : ABS_UPHOVER;
1996 break;
1997 case kPressed:
1998 state_id = ABS_UPPRESSED;
1999 break;
2000 case kDisabled:
2001 state_id = ABS_UPDISABLED;
2002 break;
2003 default:
2004 NOTREACHED() << "Invalid state: " << state;
2005 break;
2006 }
2007 break;
2008 case kScrollbarHorizontalThumb:
2009 case kScrollbarVerticalThumb:
2010 switch (state) {
2011 case kNormal:
2012 state_id = SCRBS_NORMAL;
2013 break;
2014 case kHovered:
2015 // Mimic WebKit's behaviour in ScrollbarThemeChromiumWin.cpp.
2016 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
2017 SCRBS_HOT : SCRBS_HOVER;
2018 break;
2019 case kPressed:
2020 state_id = SCRBS_PRESSED;
2021 break;
2022 case kDisabled:
2023 state_id = SCRBS_DISABLED;
2024 break;
2025 default:
2026 NOTREACHED() << "Invalid state: " << state;
2027 break;
2028 }
2029 break;
2030 default:
2031 NOTREACHED() << "Invalid part: " << part;
2032 break;
2033 }
2034 return state_id;
2035 }
2036
GetThemeInt(ThemeName theme,int part_id,int state_id,int prop_id,int * value) const2037 HRESULT NativeThemeWin::GetThemeInt(ThemeName theme,
2038 int part_id,
2039 int state_id,
2040 int prop_id,
2041 int *value) const {
2042 HANDLE handle = GetThemeHandle(theme);
2043 if (handle && get_theme_int_)
2044 return get_theme_int_(handle, part_id, state_id, prop_id, value);
2045 return E_NOTIMPL;
2046 }
2047
PaintFrameControl(HDC hdc,const gfx::Rect & rect,UINT type,UINT state,bool is_selected,State control_state) const2048 HRESULT NativeThemeWin::PaintFrameControl(HDC hdc,
2049 const gfx::Rect& rect,
2050 UINT type,
2051 UINT state,
2052 bool is_selected,
2053 State control_state) const {
2054 const int width = rect.width();
2055 const int height = rect.height();
2056
2057 // DrawFrameControl for menu arrow/check wants a monochrome bitmap.
2058 base::win::ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL));
2059
2060 if (mask_bitmap == NULL)
2061 return E_OUTOFMEMORY;
2062
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);
2067
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.
2072 int bg_color_key;
2073 int text_color_key;
2074 switch (control_state) {
2075 case NativeTheme::kHovered:
2076 bg_color_key = COLOR_HIGHLIGHT;
2077 text_color_key = COLOR_HIGHLIGHTTEXT;
2078 break;
2079 case NativeTheme::kNormal:
2080 bg_color_key = COLOR_MENU;
2081 text_color_key = COLOR_MENUTEXT;
2082 break;
2083 case NativeTheme::kDisabled:
2084 bg_color_key = is_selected ? COLOR_HIGHLIGHT : COLOR_MENU;
2085 text_color_key = COLOR_GRAYTEXT;
2086 break;
2087 default:
2088 NOTREACHED();
2089 bg_color_key = COLOR_MENU;
2090 text_color_key = COLOR_MENUTEXT;
2091 break;
2092 }
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);
2098
2099 return S_OK;
2100 }
2101
GetThemeHandle(ThemeName theme_name) const2102 HANDLE NativeThemeWin::GetThemeHandle(ThemeName theme_name) const {
2103 if (!open_theme_ || theme_name < 0 || theme_name >= LAST)
2104 return 0;
2105
2106 if (theme_handles_[theme_name])
2107 return theme_handles_[theme_name];
2108
2109 // Not found, try to load it.
2110 HANDLE handle = 0;
2111 switch (theme_name) {
2112 case BUTTON:
2113 handle = open_theme_(NULL, L"Button");
2114 break;
2115 case LIST:
2116 handle = open_theme_(NULL, L"Listview");
2117 break;
2118 case MENU:
2119 handle = open_theme_(NULL, L"Menu");
2120 break;
2121 case MENULIST:
2122 handle = open_theme_(NULL, L"Combobox");
2123 break;
2124 case SCROLLBAR:
2125 handle = open_theme_(NULL, L"Scrollbar");
2126 break;
2127 case STATUS:
2128 handle = open_theme_(NULL, L"Status");
2129 break;
2130 case TAB:
2131 handle = open_theme_(NULL, L"Tab");
2132 break;
2133 case TEXTFIELD:
2134 handle = open_theme_(NULL, L"Edit");
2135 break;
2136 case TRACKBAR:
2137 handle = open_theme_(NULL, L"Trackbar");
2138 break;
2139 case WINDOW:
2140 handle = open_theme_(NULL, L"Window");
2141 break;
2142 case PROGRESS:
2143 handle = open_theme_(NULL, L"Progress");
2144 break;
2145 case SPIN:
2146 handle = open_theme_(NULL, L"Spin");
2147 break;
2148 default:
2149 NOTREACHED();
2150 }
2151 theme_handles_[theme_name] = handle;
2152 return handle;
2153 }
2154
2155 } // namespace ui
2156