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 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 // 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 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 134 bool NativeThemeWin::IsThemingActive() const { 135 if (is_theme_active_) 136 return !!is_theme_active_(); 137 return false; 138 } 139 140 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 152 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 169 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 180 gfx::Size NativeThemeWin::GetThemeBorderSize(ThemeName theme) const { 181 // For simplicity use the wildcard state==0, part==0, since it works 182 // for the cases we currently depend on. 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 190 void NativeThemeWin::DisableTheming() const { 191 if (!set_theme_properties_) 192 return; 193 set_theme_properties_(0); 194 } 195 196 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 208 bool NativeThemeWin::IsClassicTheme(ThemeName name) const { 209 if (!theme_dll_) 210 return true; 211 212 return !GetThemeHandle(name); 213 } 214 215 // static 216 NativeThemeWin* NativeThemeWin::instance() { 217 CR_DEFINE_STATIC_LOCAL(NativeThemeWin, s_native_theme, ()); 218 return &s_native_theme; 219 } 220 221 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 277 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 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 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 392 void NativeThemeWin::OnSysColorChange() { 393 UpdateSystemColors(); 394 is_using_high_contrast_valid_ = false; 395 NotifyObservers(); 396 } 397 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 405 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 496 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 652 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 732 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 746 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 834 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 853 HRESULT NativeThemeWin::PaintMenuGutter(HDC hdc, 854 const gfx::Rect& rect) const { 855 RECT rect_win = rect.ToRECT(); 856 HANDLE handle = GetThemeHandle(MENU); 857 if (handle && draw_theme_) 858 return draw_theme_(handle, hdc, MENU_POPUPGUTTER, MPI_NORMAL, &rect_win, 859 NULL); 860 return E_NOTIMPL; 861 } 862 863 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 915 HRESULT NativeThemeWin::PaintMenuBackground(HDC hdc, 916 const gfx::Rect& rect) const { 917 HANDLE handle = GetThemeHandle(MENU); 918 RECT rect_win = rect.ToRECT(); 919 if (handle && draw_theme_) { 920 HRESULT result = draw_theme_(handle, hdc, MENU_POPUPBACKGROUND, 0, 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 931 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 953 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 966 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 997 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 1025 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 1053 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 1089 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 1124 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 1206 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 1263 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 1324 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 1358 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 1465 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 1562 HRESULT NativeThemeWin::PaintWindowResizeGripper(HDC hdc, 1563 const gfx::Rect& rect) const { 1564 HANDLE handle = GetThemeHandle(STATUS); 1565 RECT rect_win = rect.ToRECT(); 1566 if (handle && draw_theme_) { 1567 // Paint the status bar gripper. There doesn't seem to be a 1568 // standard gripper in Windows for the space between 1569 // scrollbars. This is pretty close, but it's supposed to be 1570 // painted over a status bar. 1571 return draw_theme_(handle, hdc, SP_GRIPPER, 0, &rect_win, NULL); 1572 } 1573 1574 // Draw a windows classic scrollbar gripper. 1575 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); 1576 return S_OK; 1577 } 1578 1579 HRESULT NativeThemeWin::PaintTabPanelBackground(HDC hdc, 1580 const gfx::Rect& rect) const { 1581 HANDLE handle = GetThemeHandle(TAB); 1582 RECT rect_win = rect.ToRECT(); 1583 if (handle && draw_theme_) 1584 return draw_theme_(handle, hdc, TABP_BODY, 0, &rect_win, NULL); 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 1591 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 1630 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 1684 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 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 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 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 2037 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 2048 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 2102 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