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_handle.h" 15 #include "base/memory/scoped_ptr.h" 16 #include "base/win/scoped_gdi_object.h" 17 #include "base/win/scoped_hdc.h" 18 #include "base/win/scoped_select_object.h" 19 #include "base/win/windows_version.h" 20 #include "skia/ext/bitmap_platform_device.h" 21 #include "skia/ext/platform_canvas.h" 22 #include "skia/ext/skia_utils_win.h" 23 #include "third_party/skia/include/core/SkCanvas.h" 24 #include "third_party/skia/include/core/SkColorPriv.h" 25 #include "third_party/skia/include/core/SkShader.h" 26 #include "ui/gfx/color_utils.h" 27 #include "ui/gfx/gdi_util.h" 28 #include "ui/gfx/rect.h" 29 #include "ui/gfx/rect_conversions.h" 30 #include "ui/gfx/sys_color_change_listener.h" 31 #include "ui/gfx/win/dpi.h" 32 #include "ui/native_theme/common_theme.h" 33 34 // This was removed from Winvers.h but is still used. 35 #if !defined(COLOR_MENUHIGHLIGHT) 36 #define COLOR_MENUHIGHLIGHT 29 37 #endif 38 39 namespace { 40 41 // TODO: Obtain the correct colors using GetSysColor. 42 // Theme colors returned by GetSystemColor(). 43 const SkColor kInvalidColorIdColor = SkColorSetRGB(255, 0, 128); 44 // Dialogs: 45 const SkColor kDialogBackgroundColor = SkColorSetRGB(251, 251, 251); 46 // FocusableBorder: 47 const SkColor kFocusedBorderColor = SkColorSetRGB(0x4d, 0x90, 0xfe); 48 const SkColor kUnfocusedBorderColor = SkColorSetRGB(0xd9, 0xd9, 0xd9); 49 // Button: 50 const SkColor kButtonBackgroundColor = SkColorSetRGB(0xde, 0xde, 0xde); 51 const SkColor kButtonHighlightColor = SkColorSetARGB(200, 255, 255, 255); 52 const SkColor kButtonHoverColor = SkColorSetRGB(6, 45, 117); 53 // MenuItem: 54 const SkColor kEnabledMenuItemForegroundColor = SkColorSetRGB(6, 45, 117); 55 const SkColor kDisabledMenuItemForegroundColor = SkColorSetRGB(161, 161, 146); 56 const SkColor kFocusedMenuItemBackgroundColor = SkColorSetRGB(246, 249, 253); 57 const SkColor kMenuSeparatorColor = SkColorSetARGB(50, 0, 0, 0); 58 // Table: 59 const SkColor kTreeSelectionBackgroundUnfocused = SkColorSetRGB(240, 240, 240); 60 61 // Windows system color IDs cached and updated by the native theme. 62 const int kSystemColors[] = { 63 COLOR_3DFACE, 64 COLOR_BTNTEXT, 65 COLOR_GRAYTEXT, 66 COLOR_HIGHLIGHT, 67 COLOR_HIGHLIGHTTEXT, 68 COLOR_SCROLLBAR, 69 COLOR_WINDOW, 70 COLOR_WINDOWTEXT, 71 COLOR_BTNFACE, 72 COLOR_MENUHIGHLIGHT, 73 }; 74 75 void SetCheckerboardShader(SkPaint* paint, const RECT& align_rect) { 76 // Create a 2x2 checkerboard pattern using the 3D face and highlight colors. 77 const SkColor face = color_utils::GetSysSkColor(COLOR_3DFACE); 78 const SkColor highlight = color_utils::GetSysSkColor(COLOR_3DHILIGHT); 79 SkColor buffer[] = { face, highlight, highlight, face }; 80 // Confusing bit: we first create a temporary bitmap with our desired pattern, 81 // then copy it to another bitmap. The temporary bitmap doesn't take 82 // ownership of the pixel data, and so will point to garbage when this 83 // function returns. The copy will copy the pixel data into a place owned by 84 // the bitmap, which is in turn owned by the shader, etc., so it will live 85 // until we're done using it. 86 SkBitmap temp_bitmap; 87 temp_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2); 88 temp_bitmap.setPixels(buffer); 89 SkBitmap bitmap; 90 temp_bitmap.copyTo(&bitmap, temp_bitmap.config()); 91 skia::RefPtr<SkShader> shader = skia::AdoptRef( 92 SkShader::CreateBitmapShader( 93 bitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); 94 95 // Align the pattern with the upper corner of |align_rect|. 96 SkMatrix matrix; 97 matrix.setTranslate(SkIntToScalar(align_rect.left), 98 SkIntToScalar(align_rect.top)); 99 shader->setLocalMatrix(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 // Returns true if using a high contrast theme. 131 bool UsingHighContrastTheme() { 132 HIGHCONTRAST result; 133 result.cbSize = sizeof(HIGHCONTRAST); 134 return SystemParametersInfo(SPI_GETHIGHCONTRAST, result.cbSize, &result, 0) && 135 (result.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON; 136 } 137 138 } // namespace 139 140 namespace ui { 141 142 bool NativeThemeWin::IsThemingActive() const { 143 if (is_theme_active_) 144 return !!is_theme_active_(); 145 return false; 146 } 147 148 HRESULT NativeThemeWin::GetThemeColor(ThemeName theme, 149 int part_id, 150 int state_id, 151 int prop_id, 152 SkColor* color) const { 153 HANDLE handle = GetThemeHandle(theme); 154 if (handle && get_theme_color_) { 155 COLORREF color_ref; 156 if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) == 157 S_OK) { 158 *color = skia::COLORREFToSkColor(color_ref); 159 return S_OK; 160 } 161 } 162 return E_NOTIMPL; 163 } 164 165 SkColor NativeThemeWin::GetThemeColorWithDefault(ThemeName theme, 166 int part_id, 167 int state_id, 168 int prop_id, 169 int default_sys_color) const { 170 SkColor color; 171 if (GetThemeColor(theme, part_id, state_id, prop_id, &color) != S_OK) 172 color = color_utils::GetSysSkColor(default_sys_color); 173 return color; 174 } 175 176 gfx::Size NativeThemeWin::GetThemeBorderSize(ThemeName theme) const { 177 // For simplicity use the wildcard state==0, part==0, since it works 178 // for the cases we currently depend on. 179 int border; 180 if (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK) 181 return gfx::Size(border, border); 182 else 183 return gfx::Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE)); 184 } 185 186 void NativeThemeWin::DisableTheming() const { 187 if (!set_theme_properties_) 188 return; 189 set_theme_properties_(0); 190 } 191 192 void NativeThemeWin::CloseHandles() const { 193 if (!close_theme_) 194 return; 195 196 for (int i = 0; i < LAST; ++i) { 197 if (theme_handles_[i]) { 198 close_theme_(theme_handles_[i]); 199 theme_handles_[i] = NULL; 200 } 201 } 202 } 203 204 bool NativeThemeWin::IsClassicTheme(ThemeName name) const { 205 if (!theme_dll_) 206 return true; 207 208 return !GetThemeHandle(name); 209 } 210 211 // TODO(sky): seems like we should default to NativeThemeWin, but that currently 212 // breaks a couple of tests (FocusTraversalTest.NormalTraversal in 213 // views_unittests). 214 #if !defined(USE_AURA) 215 // static 216 NativeTheme* NativeTheme::instance() { 217 return NativeThemeWin::instance(); 218 } 219 #endif 220 221 // static 222 NativeThemeWin* NativeThemeWin::instance() { 223 CR_DEFINE_STATIC_LOCAL(NativeThemeWin, s_native_theme, ()); 224 return &s_native_theme; 225 } 226 227 gfx::Size NativeThemeWin::GetPartSize(Part part, 228 State state, 229 const ExtraParams& extra) const { 230 gfx::Size part_size = CommonThemeGetPartSize(part, state, extra); 231 if (!part_size.IsEmpty()) 232 return part_size; 233 234 // The GetThemePartSize call below returns the default size without 235 // accounting for user customization (crbug/218291). 236 SIZE size; 237 switch (part) { 238 case kScrollbarDownArrow: 239 case kScrollbarLeftArrow: 240 case kScrollbarRightArrow: 241 case kScrollbarUpArrow: 242 case kScrollbarHorizontalThumb: 243 case kScrollbarVerticalThumb: 244 case kScrollbarHorizontalTrack: 245 case kScrollbarVerticalTrack: 246 size.cx = size.cy = gfx::win::GetSystemMetricsInDIP(SM_CXVSCROLL); 247 return gfx::Size(size.cx, size.cy); 248 } 249 250 int part_id = GetWindowsPart(part, state, extra); 251 int state_id = GetWindowsState(part, state, extra); 252 253 HDC hdc = GetDC(NULL); 254 HRESULT hr = GetThemePartSize(GetThemeName(part), hdc, part_id, state_id, 255 NULL, TS_TRUE, &size); 256 ReleaseDC(NULL, hdc); 257 258 if (FAILED(hr)) { 259 // TODO(rogerta): For now, we need to support radio buttons and checkboxes 260 // when theming is not enabled. Support for other parts can be added 261 // if/when needed. 262 switch (part) { 263 case kCheckbox: 264 case kRadio: 265 // TODO(rogerta): I was not able to find any API to get the default 266 // size of these controls, so determined these values empirically. 267 size.cx = 13; 268 size.cy = 13; 269 break; 270 default: 271 size.cx = 0; 272 size.cy = 0; 273 break; 274 } 275 } 276 277 return gfx::Size(size.cx, size.cy); 278 } 279 280 void NativeThemeWin::Paint(SkCanvas* canvas, 281 Part part, 282 State state, 283 const gfx::Rect& rect, 284 const ExtraParams& extra) const { 285 if (rect.IsEmpty()) 286 return; 287 288 switch (part) { 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 if (theme_dll_) { 354 draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>( 355 GetProcAddress(theme_dll_, "DrawThemeBackground")); 356 draw_theme_ex_ = reinterpret_cast<DrawThemeBackgroundExPtr>( 357 GetProcAddress(theme_dll_, "DrawThemeBackgroundEx")); 358 get_theme_color_ = reinterpret_cast<GetThemeColorPtr>( 359 GetProcAddress(theme_dll_, "GetThemeColor")); 360 get_theme_content_rect_ = reinterpret_cast<GetThemeContentRectPtr>( 361 GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect")); 362 get_theme_part_size_ = reinterpret_cast<GetThemePartSizePtr>( 363 GetProcAddress(theme_dll_, "GetThemePartSize")); 364 open_theme_ = reinterpret_cast<OpenThemeDataPtr>( 365 GetProcAddress(theme_dll_, "OpenThemeData")); 366 close_theme_ = reinterpret_cast<CloseThemeDataPtr>( 367 GetProcAddress(theme_dll_, "CloseThemeData")); 368 set_theme_properties_ = reinterpret_cast<SetThemeAppPropertiesPtr>( 369 GetProcAddress(theme_dll_, "SetThemeAppProperties")); 370 is_theme_active_ = reinterpret_cast<IsThemeActivePtr>( 371 GetProcAddress(theme_dll_, "IsThemeActive")); 372 get_theme_int_ = reinterpret_cast<GetThemeIntPtr>( 373 GetProcAddress(theme_dll_, "GetThemeInt")); 374 } 375 memset(theme_handles_, 0, sizeof(theme_handles_)); 376 377 // Initialize the cached system colors. 378 UpdateSystemColors(); 379 } 380 381 NativeThemeWin::~NativeThemeWin() { 382 if (theme_dll_) { 383 // todo (cpu): fix this soon. Making a call to CloseHandles() here breaks 384 // certain tests and the reliability bots. 385 // CloseHandles(); 386 FreeLibrary(theme_dll_); 387 } 388 } 389 390 void NativeThemeWin::OnSysColorChange() { 391 UpdateSystemColors(); 392 } 393 394 void NativeThemeWin::UpdateSystemColors() { 395 for (int i = 0; i < arraysize(kSystemColors); ++i) { 396 system_colors_[kSystemColors[i]] = 397 color_utils::GetSysSkColor(kSystemColors[i]); 398 } 399 } 400 401 void NativeThemeWin::PaintDirect(SkCanvas* canvas, 402 Part part, 403 State state, 404 const gfx::Rect& rect, 405 const ExtraParams& extra) const { 406 skia::ScopedPlatformPaint scoped_platform_paint(canvas); 407 HDC hdc = scoped_platform_paint.GetPlatformSurface(); 408 409 switch (part) { 410 case kCheckbox: 411 PaintCheckbox(hdc, part, state, rect, extra.button); 412 break; 413 case kRadio: 414 PaintRadioButton(hdc, part, state, rect, extra.button); 415 break; 416 case kPushButton: 417 PaintPushButton(hdc, part, state, rect, extra.button); 418 break; 419 case kMenuPopupArrow: 420 PaintMenuArrow(hdc, state, rect, extra.menu_arrow); 421 break; 422 case kMenuPopupGutter: 423 PaintMenuGutter(hdc, rect); 424 break; 425 case kMenuPopupSeparator: 426 PaintMenuSeparator(hdc, rect, extra.menu_separator); 427 break; 428 case kMenuPopupBackground: 429 PaintMenuBackground(hdc, rect); 430 break; 431 case kMenuCheck: 432 PaintMenuCheck(hdc, state, rect, extra.menu_check); 433 break; 434 case kMenuCheckBackground: 435 PaintMenuCheckBackground(hdc, state, rect); 436 break; 437 case kMenuItemBackground: 438 PaintMenuItemBackground(hdc, state, rect, extra.menu_item); 439 break; 440 case kMenuList: 441 PaintMenuList(hdc, state, rect, extra.menu_list); 442 break; 443 case kScrollbarDownArrow: 444 case kScrollbarUpArrow: 445 case kScrollbarLeftArrow: 446 case kScrollbarRightArrow: 447 PaintScrollbarArrow(hdc, part, state, rect, extra.scrollbar_arrow); 448 break; 449 case kScrollbarHorizontalTrack: 450 case kScrollbarVerticalTrack: 451 PaintScrollbarTrack(canvas, hdc, part, state, rect, 452 extra.scrollbar_track); 453 break; 454 case kScrollbarHorizontalThumb: 455 case kScrollbarVerticalThumb: 456 case kScrollbarHorizontalGripper: 457 case kScrollbarVerticalGripper: 458 PaintScrollbarThumb(hdc, part, state, rect, extra.scrollbar_thumb); 459 break; 460 case kInnerSpinButton: 461 PaintSpinButton(hdc, part, state, rect, extra.inner_spin); 462 break; 463 case kTrackbarThumb: 464 case kTrackbarTrack: 465 PaintTrackbar(canvas, hdc, part, state, rect, extra.trackbar); 466 break; 467 case kProgressBar: 468 PaintProgressBar(hdc, rect, extra.progress_bar); 469 break; 470 case kWindowResizeGripper: 471 PaintWindowResizeGripper(hdc, rect); 472 break; 473 case kTabPanelBackground: 474 PaintTabPanelBackground(hdc, rect); 475 break; 476 case kTextField: 477 PaintTextField(hdc, part, state, rect, extra.text_field); 478 break; 479 480 case kSliderTrack: 481 case kSliderThumb: 482 default: 483 // While transitioning NativeThemeWin to the single Paint() entry point, 484 // unsupported parts will DCHECK here. 485 NOTREACHED(); 486 } 487 } 488 489 SkColor NativeThemeWin::GetSystemColor(ColorId color_id) const { 490 SkColor color; 491 if (CommonThemeGetSystemColor(color_id, &color)) 492 return color; 493 494 switch (color_id) { 495 // Windows 496 case kColorId_WindowBackground: 497 return system_colors_[COLOR_WINDOW]; 498 499 // Dialogs 500 case kColorId_DialogBackground: 501 if (gfx::IsInvertedColorScheme()) 502 return color_utils::InvertColor(kDialogBackgroundColor); 503 return kDialogBackgroundColor; 504 505 // FocusableBorder 506 case kColorId_FocusedBorderColor: 507 return kFocusedBorderColor; 508 case kColorId_UnfocusedBorderColor: 509 return kUnfocusedBorderColor; 510 511 // Button 512 case kColorId_ButtonBackgroundColor: 513 return kButtonBackgroundColor; 514 case kColorId_ButtonEnabledColor: 515 return system_colors_[COLOR_BTNTEXT]; 516 case kColorId_ButtonDisabledColor: 517 return system_colors_[COLOR_GRAYTEXT]; 518 case kColorId_ButtonHighlightColor: 519 return kButtonHighlightColor; 520 case kColorId_ButtonHoverColor: 521 return kButtonHoverColor; 522 523 // MenuItem 524 case kColorId_EnabledMenuItemForegroundColor: 525 return kEnabledMenuItemForegroundColor; 526 case kColorId_DisabledMenuItemForegroundColor: 527 return kDisabledMenuItemForegroundColor; 528 case kColorId_FocusedMenuItemBackgroundColor: 529 return kFocusedMenuItemBackgroundColor; 530 case kColorId_MenuSeparatorColor: 531 return kMenuSeparatorColor; 532 533 // Label 534 case kColorId_LabelEnabledColor: 535 return system_colors_[COLOR_BTNTEXT]; 536 case kColorId_LabelDisabledColor: 537 return system_colors_[COLOR_GRAYTEXT]; 538 case kColorId_LabelBackgroundColor: 539 return system_colors_[COLOR_WINDOW]; 540 541 // Textfield 542 case kColorId_TextfieldDefaultColor: 543 return system_colors_[COLOR_WINDOWTEXT]; 544 case kColorId_TextfieldDefaultBackground: 545 return system_colors_[COLOR_WINDOW]; 546 case kColorId_TextfieldReadOnlyColor: 547 return system_colors_[COLOR_GRAYTEXT]; 548 case kColorId_TextfieldReadOnlyBackground: 549 return system_colors_[COLOR_3DFACE]; 550 case kColorId_TextfieldSelectionColor: 551 return system_colors_[COLOR_HIGHLIGHTTEXT]; 552 case kColorId_TextfieldSelectionBackgroundFocused: 553 return system_colors_[COLOR_HIGHLIGHT]; 554 555 // Tree 556 // NOTE: these aren't right for all themes, but as close as I could get. 557 case kColorId_TreeBackground: 558 return system_colors_[COLOR_WINDOW]; 559 case kColorId_TreeText: 560 return system_colors_[COLOR_WINDOWTEXT]; 561 case kColorId_TreeSelectedText: 562 return system_colors_[COLOR_HIGHLIGHTTEXT]; 563 case kColorId_TreeSelectedTextUnfocused: 564 return system_colors_[COLOR_BTNTEXT]; 565 case kColorId_TreeSelectionBackgroundFocused: 566 return system_colors_[COLOR_HIGHLIGHT]; 567 case kColorId_TreeSelectionBackgroundUnfocused: 568 return system_colors_[UsingHighContrastTheme() ? 569 COLOR_MENUHIGHLIGHT : COLOR_BTNFACE]; 570 case kColorId_TreeArrow: 571 return system_colors_[COLOR_WINDOWTEXT]; 572 573 // Table 574 case kColorId_TableBackground: 575 return system_colors_[COLOR_WINDOW]; 576 case kColorId_TableText: 577 return system_colors_[COLOR_WINDOWTEXT]; 578 case kColorId_TableSelectedText: 579 return system_colors_[COLOR_HIGHLIGHTTEXT]; 580 case kColorId_TableSelectedTextUnfocused: 581 return system_colors_[COLOR_BTNTEXT]; 582 case kColorId_TableSelectionBackgroundFocused: 583 return system_colors_[COLOR_HIGHLIGHT]; 584 case kColorId_TableSelectionBackgroundUnfocused: 585 return system_colors_[UsingHighContrastTheme() ? 586 COLOR_MENUHIGHLIGHT : COLOR_BTNFACE]; 587 case kColorId_TableGroupingIndicatorColor: 588 return system_colors_[COLOR_GRAYTEXT]; 589 590 default: 591 NOTREACHED(); 592 break; 593 } 594 return kInvalidColorIdColor; 595 } 596 597 void NativeThemeWin::PaintIndirect(SkCanvas* canvas, 598 Part part, 599 State state, 600 const gfx::Rect& rect, 601 const ExtraParams& extra) const { 602 // TODO(asvitkine): This path is pretty inefficient - for each paint operation 603 // it creates a new offscreen bitmap Skia canvas. This can 604 // be sped up by doing it only once per part/state and 605 // keeping a cache of the resulting bitmaps. 606 607 // Create an offscreen canvas that is backed by an HDC. 608 skia::RefPtr<skia::BitmapPlatformDevice> device = skia::AdoptRef( 609 skia::BitmapPlatformDevice::Create( 610 rect.width(), rect.height(), false, NULL)); 611 DCHECK(device); 612 SkCanvas offscreen_canvas(device.get()); 613 DCHECK(skia::SupportsPlatformPaint(&offscreen_canvas)); 614 615 // Some of the Windows theme drawing operations do not write correct alpha 616 // values for fully-opaque pixels; instead the pixels get alpha 0. This is 617 // especially a problem on Windows XP or when using the Classic theme. 618 // 619 // To work-around this, mark all pixels with a placeholder value, to detect 620 // which pixels get touched by the paint operation. After paint, set any 621 // pixels that have alpha 0 to opaque and placeholders to fully-transparent. 622 const SkColor placeholder = SkColorSetARGB(1, 0, 0, 0); 623 offscreen_canvas.clear(placeholder); 624 625 // Offset destination rects to have origin (0,0). 626 gfx::Rect adjusted_rect(rect.size()); 627 ExtraParams adjusted_extra(extra); 628 switch (part) { 629 case kProgressBar: 630 adjusted_extra.progress_bar.value_rect_x = 0; 631 adjusted_extra.progress_bar.value_rect_y = 0; 632 break; 633 case kScrollbarHorizontalTrack: 634 case kScrollbarVerticalTrack: 635 adjusted_extra.scrollbar_track.track_x = 0; 636 adjusted_extra.scrollbar_track.track_y = 0; 637 break; 638 default: break; 639 } 640 // Draw the theme controls using existing HDC-drawing code. 641 PaintDirect(&offscreen_canvas, 642 part, 643 state, 644 adjusted_rect, 645 adjusted_extra); 646 647 // Copy the pixels to a bitmap that has ref-counted pixel storage, which is 648 // necessary to have when drawing to a SkPicture. 649 const SkBitmap& hdc_bitmap = 650 offscreen_canvas.getDevice()->accessBitmap(false); 651 SkBitmap bitmap; 652 hdc_bitmap.copyTo(&bitmap, SkBitmap::kARGB_8888_Config); 653 654 // Post-process the pixels to fix up the alpha values (see big comment above). 655 const SkPMColor placeholder_value = SkPreMultiplyColor(placeholder); 656 const int pixel_count = rect.width() * rect.height(); 657 SkPMColor* pixels = bitmap.getAddr32(0, 0); 658 for (int i = 0; i < pixel_count; i++) { 659 if (pixels[i] == placeholder_value) { 660 // Pixel wasn't touched - make it fully transparent. 661 pixels[i] = SkPackARGB32(0, 0, 0, 0); 662 } else if (SkGetPackedA32(pixels[i]) == 0) { 663 // Pixel was touched but has incorrect alpha of 0, make it fully opaque. 664 pixels[i] = SkPackARGB32(0xFF, 665 SkGetPackedR32(pixels[i]), 666 SkGetPackedG32(pixels[i]), 667 SkGetPackedB32(pixels[i])); 668 } 669 } 670 671 // Draw the offscreen bitmap to the destination canvas. 672 canvas->drawBitmap(bitmap, rect.x(), rect.y()); 673 } 674 675 HRESULT NativeThemeWin::GetThemePartSize(ThemeName theme_name, 676 HDC hdc, 677 int part_id, 678 int state_id, 679 RECT* rect, 680 int ts, 681 SIZE* size) const { 682 HANDLE handle = GetThemeHandle(theme_name); 683 if (handle && get_theme_part_size_) 684 return get_theme_part_size_(handle, hdc, part_id, state_id, rect, ts, size); 685 686 return E_NOTIMPL; 687 } 688 689 HRESULT NativeThemeWin::PaintButton(HDC hdc, 690 State state, 691 const ButtonExtraParams& extra, 692 int part_id, 693 int state_id, 694 RECT* rect) const { 695 HANDLE handle = GetThemeHandle(BUTTON); 696 if (handle && draw_theme_) 697 return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); 698 699 // Adjust classic_state based on part, state, and extras. 700 int classic_state = extra.classic_state; 701 switch (part_id) { 702 case BP_CHECKBOX: 703 classic_state |= DFCS_BUTTONCHECK; 704 break; 705 case BP_RADIOBUTTON: 706 classic_state |= DFCS_BUTTONRADIO; 707 break; 708 case BP_PUSHBUTTON: 709 classic_state |= DFCS_BUTTONPUSH; 710 break; 711 default: 712 NOTREACHED() << "Unknown part_id: " << part_id; 713 break; 714 } 715 716 switch (state) { 717 case kDisabled: 718 classic_state |= DFCS_INACTIVE; 719 break; 720 case kPressed: 721 classic_state |= DFCS_PUSHED; 722 break; 723 case kNormal: 724 case kHovered: 725 break; 726 default: 727 NOTREACHED() << "Unknown state: " << state; 728 break; 729 } 730 731 if (extra.checked) 732 classic_state |= DFCS_CHECKED; 733 734 // Draw it manually. 735 // All pressed states have both low bits set, and no other states do. 736 const bool focused = ((state_id & ETS_FOCUSED) == ETS_FOCUSED); 737 const bool pressed = ((state_id & PBS_PRESSED) == PBS_PRESSED); 738 if ((BP_PUSHBUTTON == part_id) && (pressed || focused)) { 739 // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the 740 // button itself is shrunk by 1 pixel. 741 HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW); 742 if (brush) { 743 FrameRect(hdc, rect, brush); 744 InflateRect(rect, -1, -1); 745 } 746 } 747 DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state); 748 749 // Draw the focus rectangle (the dotted line box) only on buttons. For radio 750 // and checkboxes, we let webkit draw the focus rectangle (orange glow). 751 if ((BP_PUSHBUTTON == part_id) && focused) { 752 // The focus rect is inside the button. The exact number of pixels depends 753 // on whether we're in classic mode or using uxtheme. 754 if (handle && get_theme_content_rect_) { 755 get_theme_content_rect_(handle, hdc, part_id, state_id, rect, rect); 756 } else { 757 InflateRect(rect, -GetSystemMetrics(SM_CXEDGE), 758 -GetSystemMetrics(SM_CYEDGE)); 759 } 760 DrawFocusRect(hdc, rect); 761 } 762 763 // Classic theme doesn't support indeterminate checkboxes. We draw 764 // a recangle inside a checkbox like IE10 does. 765 if (part_id == BP_CHECKBOX && extra.indeterminate) { 766 RECT inner_rect = *rect; 767 // "4 / 13" is same as IE10 in classic theme. 768 int padding = (inner_rect.right - inner_rect.left) * 4 / 13; 769 InflateRect(&inner_rect, -padding, -padding); 770 int color_index = state == kDisabled ? COLOR_GRAYTEXT : COLOR_WINDOWTEXT; 771 FillRect(hdc, &inner_rect, GetSysColorBrush(color_index)); 772 } 773 774 return S_OK; 775 } 776 777 HRESULT NativeThemeWin::PaintMenuSeparator( 778 HDC hdc, 779 const gfx::Rect& rect, 780 const MenuSeparatorExtraParams& extra) const { 781 RECT rect_win = rect.ToRECT(); 782 783 HANDLE handle = GetThemeHandle(MENU); 784 if (handle && draw_theme_) { 785 // Delta is needed for non-classic to move separator up slightly. 786 --rect_win.top; 787 --rect_win.bottom; 788 return draw_theme_(handle, hdc, MENU_POPUPSEPARATOR, MPI_NORMAL, &rect_win, 789 NULL); 790 } 791 792 DrawEdge(hdc, &rect_win, EDGE_ETCHED, BF_TOP); 793 return S_OK; 794 } 795 796 HRESULT NativeThemeWin::PaintMenuGutter(HDC hdc, 797 const gfx::Rect& rect) const { 798 RECT rect_win = rect.ToRECT(); 799 HANDLE handle = GetThemeHandle(MENU); 800 if (handle && draw_theme_) 801 return draw_theme_(handle, hdc, MENU_POPUPGUTTER, MPI_NORMAL, &rect_win, 802 NULL); 803 return E_NOTIMPL; 804 } 805 806 HRESULT NativeThemeWin::PaintMenuArrow(HDC hdc, 807 State state, 808 const gfx::Rect& rect, 809 const MenuArrowExtraParams& extra) 810 const { 811 int state_id = MSM_NORMAL; 812 if (state == kDisabled) 813 state_id = MSM_DISABLED; 814 815 HANDLE handle = GetThemeHandle(MENU); 816 RECT rect_win = rect.ToRECT(); 817 if (handle && draw_theme_) { 818 if (extra.pointing_right) { 819 return draw_theme_(handle, hdc, MENU_POPUPSUBMENU, state_id, &rect_win, 820 NULL); 821 } else { 822 // There is no way to tell the uxtheme API to draw a left pointing arrow; 823 // it doesn't have a flag equivalent to DFCS_MENUARROWRIGHT. But they 824 // are needed for RTL locales on Vista. So use a memory DC and mirror 825 // the region with GDI's StretchBlt. 826 gfx::Rect r(rect); 827 base::win::ScopedCreateDC mem_dc(CreateCompatibleDC(hdc)); 828 base::win::ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(), 829 r.height())); 830 base::win::ScopedSelectObject select_bitmap(mem_dc, mem_bitmap); 831 // Copy and horizontally mirror the background from hdc into mem_dc. Use 832 // a negative-width source rect, starting at the rightmost pixel. 833 StretchBlt(mem_dc, 0, 0, r.width(), r.height(), 834 hdc, r.right()-1, r.y(), -r.width(), r.height(), SRCCOPY); 835 // Draw the arrow. 836 RECT theme_rect = {0, 0, r.width(), r.height()}; 837 HRESULT result = draw_theme_(handle, mem_dc, MENU_POPUPSUBMENU, 838 state_id, &theme_rect, NULL); 839 // Copy and mirror the result back into mem_dc. 840 StretchBlt(hdc, r.x(), r.y(), r.width(), r.height(), 841 mem_dc, r.width()-1, 0, -r.width(), r.height(), SRCCOPY); 842 return result; 843 } 844 } 845 846 // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a 847 // left pointing arrow. This makes the following 'if' statement slightly 848 // counterintuitive. 849 UINT pfc_state; 850 if (extra.pointing_right) 851 pfc_state = DFCS_MENUARROW; 852 else 853 pfc_state = DFCS_MENUARROWRIGHT; 854 return PaintFrameControl(hdc, rect, DFC_MENU, pfc_state, extra.is_selected, 855 state); 856 } 857 858 HRESULT NativeThemeWin::PaintMenuBackground(HDC hdc, 859 const gfx::Rect& rect) const { 860 HANDLE handle = GetThemeHandle(MENU); 861 RECT rect_win = rect.ToRECT(); 862 if (handle && draw_theme_) { 863 HRESULT result = draw_theme_(handle, hdc, MENU_POPUPBACKGROUND, 0, 864 &rect_win, NULL); 865 FrameRect(hdc, &rect_win, GetSysColorBrush(COLOR_3DSHADOW)); 866 return result; 867 } 868 869 FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_MENU)); 870 DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT); 871 return S_OK; 872 } 873 874 HRESULT NativeThemeWin::PaintMenuCheck( 875 HDC hdc, 876 State state, 877 const gfx::Rect& rect, 878 const MenuCheckExtraParams& extra) const { 879 HANDLE handle = GetThemeHandle(MENU); 880 int state_id; 881 if (extra.is_radio) { 882 state_id = state == kDisabled ? MC_BULLETDISABLED : MC_BULLETNORMAL; 883 } else { 884 state_id = state == kDisabled ? MC_CHECKMARKDISABLED : MC_CHECKMARKNORMAL; 885 } 886 887 RECT rect_win = rect.ToRECT(); 888 if (handle && draw_theme_) 889 return draw_theme_(handle, hdc, MENU_POPUPCHECK, state_id, &rect_win, NULL); 890 891 return PaintFrameControl(hdc, rect, DFC_MENU, 892 extra.is_radio ? DFCS_MENUBULLET : DFCS_MENUCHECK, 893 extra.is_selected, state); 894 } 895 896 HRESULT NativeThemeWin::PaintMenuCheckBackground(HDC hdc, 897 State state, 898 const gfx::Rect& rect) const { 899 HANDLE handle = GetThemeHandle(MENU); 900 int state_id = state == kDisabled ? MCB_DISABLED : MCB_NORMAL; 901 RECT rect_win = rect.ToRECT(); 902 if (handle && draw_theme_) 903 return draw_theme_(handle, hdc, MENU_POPUPCHECKBACKGROUND, state_id, 904 &rect_win, NULL); 905 // Nothing to do for background. 906 return S_OK; 907 } 908 909 HRESULT NativeThemeWin::PaintMenuItemBackground( 910 HDC hdc, 911 State state, 912 const gfx::Rect& rect, 913 const MenuItemExtraParams& extra) const { 914 HANDLE handle = GetThemeHandle(MENU); 915 RECT rect_win = rect.ToRECT(); 916 int state_id; 917 switch (state) { 918 case kNormal: 919 state_id = MPI_NORMAL; 920 break; 921 case kDisabled: 922 state_id = extra.is_selected ? MPI_DISABLEDHOT : MPI_DISABLED; 923 break; 924 case kHovered: 925 state_id = MPI_HOT; 926 break; 927 default: 928 NOTREACHED() << "Invalid state " << state; 929 break; 930 } 931 932 if (handle && draw_theme_) 933 return draw_theme_(handle, hdc, MENU_POPUPITEM, state_id, &rect_win, NULL); 934 935 if (extra.is_selected) 936 FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_HIGHLIGHT)); 937 return S_OK; 938 } 939 940 HRESULT NativeThemeWin::PaintPushButton(HDC hdc, 941 Part part, 942 State state, 943 const gfx::Rect& rect, 944 const ButtonExtraParams& extra) const { 945 int state_id; 946 switch (state) { 947 case kDisabled: 948 state_id = PBS_DISABLED; 949 break; 950 case kHovered: 951 state_id = PBS_HOT; 952 break; 953 case kNormal: 954 state_id = extra.is_default ? PBS_DEFAULTED : PBS_NORMAL; 955 break; 956 case kPressed: 957 state_id = PBS_PRESSED; 958 break; 959 default: 960 NOTREACHED() << "Invalid state: " << state; 961 break; 962 } 963 964 RECT rect_win = rect.ToRECT(); 965 return PaintButton(hdc, state, extra, BP_PUSHBUTTON, state_id, &rect_win); 966 } 967 968 HRESULT NativeThemeWin::PaintRadioButton(HDC hdc, 969 Part part, 970 State state, 971 const gfx::Rect& rect, 972 const ButtonExtraParams& extra) const { 973 int state_id; 974 switch (state) { 975 case kDisabled: 976 state_id = extra.checked ? RBS_CHECKEDDISABLED : RBS_UNCHECKEDDISABLED; 977 break; 978 case kHovered: 979 state_id = extra.checked ? RBS_CHECKEDHOT : RBS_UNCHECKEDHOT; 980 break; 981 case kNormal: 982 state_id = extra.checked ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL; 983 break; 984 case kPressed: 985 state_id = extra.checked ? RBS_CHECKEDPRESSED : RBS_UNCHECKEDPRESSED; 986 break; 987 default: 988 NOTREACHED() << "Invalid state: " << state; 989 break; 990 } 991 992 RECT rect_win = rect.ToRECT(); 993 return PaintButton(hdc, state, extra, BP_RADIOBUTTON, state_id, &rect_win); 994 } 995 996 HRESULT NativeThemeWin::PaintCheckbox(HDC hdc, 997 Part part, 998 State state, 999 const gfx::Rect& rect, 1000 const ButtonExtraParams& extra) const { 1001 int state_id; 1002 switch (state) { 1003 case kDisabled: 1004 state_id = extra.checked ? CBS_CHECKEDDISABLED : 1005 extra.indeterminate ? CBS_MIXEDDISABLED : 1006 CBS_UNCHECKEDDISABLED; 1007 break; 1008 case kHovered: 1009 state_id = extra.checked ? CBS_CHECKEDHOT : 1010 extra.indeterminate ? CBS_MIXEDHOT : 1011 CBS_UNCHECKEDHOT; 1012 break; 1013 case kNormal: 1014 state_id = extra.checked ? CBS_CHECKEDNORMAL : 1015 extra.indeterminate ? CBS_MIXEDNORMAL : 1016 CBS_UNCHECKEDNORMAL; 1017 break; 1018 case kPressed: 1019 state_id = extra.checked ? CBS_CHECKEDPRESSED : 1020 extra.indeterminate ? CBS_MIXEDPRESSED : 1021 CBS_UNCHECKEDPRESSED; 1022 break; 1023 default: 1024 NOTREACHED() << "Invalid state: " << state; 1025 break; 1026 } 1027 1028 RECT rect_win = rect.ToRECT(); 1029 return PaintButton(hdc, state, extra, BP_CHECKBOX, state_id, &rect_win); 1030 } 1031 1032 HRESULT NativeThemeWin::PaintMenuList(HDC hdc, 1033 State state, 1034 const gfx::Rect& rect, 1035 const MenuListExtraParams& extra) const { 1036 HANDLE handle = GetThemeHandle(MENULIST); 1037 RECT rect_win = rect.ToRECT(); 1038 int state_id; 1039 switch (state) { 1040 case kNormal: 1041 state_id = CBXS_NORMAL; 1042 break; 1043 case kDisabled: 1044 state_id = CBXS_DISABLED; 1045 break; 1046 case kHovered: 1047 state_id = CBXS_HOT; 1048 break; 1049 case kPressed: 1050 state_id = CBXS_PRESSED; 1051 break; 1052 default: 1053 NOTREACHED() << "Invalid state " << state; 1054 break; 1055 } 1056 1057 if (handle && draw_theme_) 1058 return draw_theme_(handle, hdc, CP_DROPDOWNBUTTON, state_id, &rect_win, 1059 NULL); 1060 1061 // Draw it manually. 1062 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, 1063 DFCS_SCROLLCOMBOBOX | extra.classic_state); 1064 return S_OK; 1065 } 1066 1067 HRESULT NativeThemeWin::PaintScrollbarArrow( 1068 HDC hdc, 1069 Part part, 1070 State state, 1071 const gfx::Rect& rect, 1072 const ScrollbarArrowExtraParams& extra) const { 1073 static const int state_id_matrix[4][kMaxState] = { 1074 ABS_DOWNDISABLED, ABS_DOWNHOT, ABS_DOWNNORMAL, ABS_DOWNPRESSED, 1075 ABS_LEFTDISABLED, ABS_LEFTHOT, ABS_LEFTNORMAL, ABS_LEFTPRESSED, 1076 ABS_RIGHTDISABLED, ABS_RIGHTHOT, ABS_RIGHTNORMAL, ABS_RIGHTPRESSED, 1077 ABS_UPDISABLED, ABS_UPHOT, ABS_UPNORMAL, ABS_UPPRESSED 1078 }; 1079 HANDLE handle = GetThemeHandle(SCROLLBAR); 1080 RECT rect_win = rect.ToRECT(); 1081 if (handle && draw_theme_) { 1082 int index = part - kScrollbarDownArrow; 1083 DCHECK(index >=0 && index < 4); 1084 int state_id = state_id_matrix[index][state]; 1085 1086 // Hovering means that the cursor is over the scroolbar, but not over the 1087 // specific arrow itself. We don't want to show it "hot" mode, but only 1088 // in "hover" mode. 1089 if (state == kHovered && extra.is_hovering) { 1090 switch (part) { 1091 case kScrollbarDownArrow: 1092 state_id = ABS_DOWNHOVER; 1093 break; 1094 case kScrollbarLeftArrow: 1095 state_id = ABS_LEFTHOVER; 1096 break; 1097 case kScrollbarRightArrow: 1098 state_id = ABS_RIGHTHOVER; 1099 break; 1100 case kScrollbarUpArrow: 1101 state_id = ABS_UPHOVER; 1102 break; 1103 default: 1104 NOTREACHED() << "Invalid part: " << part; 1105 break; 1106 } 1107 } 1108 return PaintScaledTheme(handle, hdc, SBP_ARROWBTN, state_id, rect); 1109 } 1110 1111 int classic_state = DFCS_SCROLLDOWN; 1112 switch (part) { 1113 case kScrollbarDownArrow: 1114 classic_state = DFCS_SCROLLDOWN; 1115 break; 1116 case kScrollbarLeftArrow: 1117 classic_state = DFCS_SCROLLLEFT; 1118 break; 1119 case kScrollbarRightArrow: 1120 classic_state = DFCS_SCROLLRIGHT; 1121 break; 1122 case kScrollbarUpArrow: 1123 classic_state = DFCS_SCROLLUP; 1124 break; 1125 default: 1126 NOTREACHED() << "Invalid part: " << part; 1127 break; 1128 } 1129 switch (state) { 1130 case kDisabled: 1131 classic_state |= DFCS_INACTIVE; 1132 break; 1133 case kHovered: 1134 classic_state |= DFCS_HOT; 1135 break; 1136 case kNormal: 1137 break; 1138 case kPressed: 1139 classic_state |= DFCS_PUSHED; 1140 break; 1141 default: 1142 NOTREACHED() << "Invalid state: " << state; 1143 break; 1144 } 1145 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, classic_state); 1146 return S_OK; 1147 } 1148 1149 HRESULT NativeThemeWin::PaintScrollbarThumb( 1150 HDC hdc, 1151 Part part, 1152 State state, 1153 const gfx::Rect& rect, 1154 const ScrollbarThumbExtraParams& extra) const { 1155 HANDLE handle = GetThemeHandle(SCROLLBAR); 1156 RECT rect_win = rect.ToRECT(); 1157 int part_id; 1158 int state_id; 1159 1160 switch (part) { 1161 case NativeTheme::kScrollbarHorizontalThumb: 1162 part_id = SBP_THUMBBTNHORZ; 1163 break; 1164 case NativeTheme::kScrollbarVerticalThumb: 1165 part_id = SBP_THUMBBTNVERT; 1166 break; 1167 case NativeTheme::kScrollbarHorizontalGripper: 1168 part_id = SBP_GRIPPERHORZ; 1169 break; 1170 case NativeTheme::kScrollbarVerticalGripper: 1171 part_id = SBP_GRIPPERVERT; 1172 break; 1173 default: 1174 NOTREACHED() << "Invalid part: " << part; 1175 break; 1176 } 1177 1178 switch (state) { 1179 case kDisabled: 1180 state_id = SCRBS_DISABLED; 1181 break; 1182 case kHovered: 1183 state_id = extra.is_hovering ? SCRBS_HOVER : SCRBS_HOT; 1184 break; 1185 case kNormal: 1186 state_id = SCRBS_NORMAL; 1187 break; 1188 case kPressed: 1189 state_id = SCRBS_PRESSED; 1190 break; 1191 default: 1192 NOTREACHED() << "Invalid state: " << state; 1193 break; 1194 } 1195 1196 if (handle && draw_theme_) 1197 return PaintScaledTheme(handle, hdc, part_id, state_id, rect); 1198 1199 // Draw it manually. 1200 if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT)) 1201 DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_MIDDLE); 1202 // Classic mode doesn't have a gripper. 1203 return S_OK; 1204 } 1205 1206 HRESULT NativeThemeWin::PaintScrollbarTrack( 1207 SkCanvas* canvas, 1208 HDC hdc, 1209 Part part, 1210 State state, 1211 const gfx::Rect& rect, 1212 const ScrollbarTrackExtraParams& extra) const { 1213 HANDLE handle = GetThemeHandle(SCROLLBAR); 1214 RECT rect_win = rect.ToRECT(); 1215 int part_id; 1216 int state_id; 1217 1218 switch (part) { 1219 case NativeTheme::kScrollbarHorizontalTrack: 1220 part_id = extra.is_upper ? SBP_UPPERTRACKHORZ : SBP_LOWERTRACKHORZ; 1221 break; 1222 case NativeTheme::kScrollbarVerticalTrack: 1223 part_id = extra.is_upper ? SBP_UPPERTRACKVERT : SBP_LOWERTRACKVERT; 1224 break; 1225 default: 1226 NOTREACHED() << "Invalid part: " << part; 1227 break; 1228 } 1229 1230 switch (state) { 1231 case kDisabled: 1232 state_id = SCRBS_DISABLED; 1233 break; 1234 case kHovered: 1235 state_id = SCRBS_HOVER; 1236 break; 1237 case kNormal: 1238 state_id = SCRBS_NORMAL; 1239 break; 1240 case kPressed: 1241 state_id = SCRBS_PRESSED; 1242 break; 1243 default: 1244 NOTREACHED() << "Invalid state: " << state; 1245 break; 1246 } 1247 1248 if (handle && draw_theme_) 1249 return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL); 1250 1251 // Draw it manually. 1252 if ((system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_3DFACE]) && 1253 (system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_WINDOW])) { 1254 FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_SCROLLBAR + 1)); 1255 } else { 1256 SkPaint paint; 1257 RECT align_rect = gfx::Rect(extra.track_x, extra.track_y, extra.track_width, 1258 extra.track_height).ToRECT(); 1259 SetCheckerboardShader(&paint, align_rect); 1260 canvas->drawIRect(skia::RECTToSkIRect(rect_win), paint); 1261 } 1262 if (extra.classic_state & DFCS_PUSHED) 1263 InvertRect(hdc, &rect_win); 1264 return S_OK; 1265 } 1266 1267 HRESULT NativeThemeWin::PaintSpinButton( 1268 HDC hdc, 1269 Part part, 1270 State state, 1271 const gfx::Rect& rect, 1272 const InnerSpinButtonExtraParams& extra) const { 1273 HANDLE handle = GetThemeHandle(SPIN); 1274 RECT rect_win = rect.ToRECT(); 1275 int part_id = extra.spin_up ? SPNP_UP : SPNP_DOWN; 1276 int state_id; 1277 switch (state) { 1278 case kDisabled: 1279 state_id = extra.spin_up ? UPS_DISABLED : DNS_DISABLED; 1280 break; 1281 case kHovered: 1282 state_id = extra.spin_up ? UPS_HOT : DNS_HOT; 1283 break; 1284 case kNormal: 1285 state_id = extra.spin_up ? UPS_NORMAL : DNS_NORMAL; 1286 break; 1287 case kPressed: 1288 state_id = extra.spin_up ? UPS_PRESSED : DNS_PRESSED; 1289 break; 1290 default: 1291 NOTREACHED() << "Invalid state " << state; 1292 break; 1293 } 1294 1295 if (handle && draw_theme_) 1296 return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL); 1297 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, extra.classic_state); 1298 return S_OK; 1299 } 1300 1301 HRESULT NativeThemeWin::PaintTrackbar( 1302 SkCanvas* canvas, 1303 HDC hdc, 1304 Part part, 1305 State state, 1306 const gfx::Rect& rect, 1307 const TrackbarExtraParams& extra) const { 1308 int part_id = part == kTrackbarTrack ? TKP_TRACK : TKP_THUMBBOTTOM; 1309 if (extra.vertical) 1310 part_id = part == kTrackbarTrack ? TKP_TRACKVERT : TKP_THUMBVERT; 1311 1312 int state_id = 0; 1313 switch (state) { 1314 case kDisabled: 1315 state_id = TUS_DISABLED; 1316 break; 1317 case kHovered: 1318 state_id = TUS_HOT; 1319 break; 1320 case kNormal: 1321 state_id = TUS_NORMAL; 1322 break; 1323 case kPressed: 1324 state_id = TUS_PRESSED; 1325 break; 1326 default: 1327 NOTREACHED() << "Invalid state " << state; 1328 break; 1329 } 1330 1331 // Make the channel be 4 px thick in the center of the supplied rect. (4 px 1332 // matches what XP does in various menus; GetThemePartSize() doesn't seem to 1333 // return good values here.) 1334 RECT rect_win = rect.ToRECT(); 1335 RECT channel_rect = rect.ToRECT(); 1336 const int channel_thickness = 4; 1337 if (part_id == TKP_TRACK) { 1338 channel_rect.top += 1339 ((channel_rect.bottom - channel_rect.top - channel_thickness) / 2); 1340 channel_rect.bottom = channel_rect.top + channel_thickness; 1341 } else if (part_id == TKP_TRACKVERT) { 1342 channel_rect.left += 1343 ((channel_rect.right - channel_rect.left - channel_thickness) / 2); 1344 channel_rect.right = channel_rect.left + channel_thickness; 1345 } // else this isn't actually a channel, so |channel_rect| == |rect|. 1346 1347 HANDLE handle = GetThemeHandle(TRACKBAR); 1348 if (handle && draw_theme_) 1349 return draw_theme_(handle, hdc, part_id, state_id, &channel_rect, NULL); 1350 1351 // Classic mode, draw it manually. 1352 if ((part_id == TKP_TRACK) || (part_id == TKP_TRACKVERT)) { 1353 DrawEdge(hdc, &channel_rect, EDGE_SUNKEN, BF_RECT); 1354 } else if (part_id == TKP_THUMBVERT) { 1355 DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE); 1356 } else { 1357 // Split rect into top and bottom pieces. 1358 RECT top_section = rect.ToRECT(); 1359 RECT bottom_section = rect.ToRECT(); 1360 top_section.bottom -= ((bottom_section.right - bottom_section.left) / 2); 1361 bottom_section.top = top_section.bottom; 1362 DrawEdge(hdc, &top_section, EDGE_RAISED, 1363 BF_LEFT | BF_TOP | BF_RIGHT | BF_SOFT | BF_MIDDLE | BF_ADJUST); 1364 1365 // Split triangular piece into two diagonals. 1366 RECT& left_half = bottom_section; 1367 RECT right_half = bottom_section; 1368 right_half.left += ((bottom_section.right - bottom_section.left) / 2); 1369 left_half.right = right_half.left; 1370 DrawEdge(hdc, &left_half, EDGE_RAISED, 1371 BF_DIAGONAL_ENDTOPLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST); 1372 DrawEdge(hdc, &right_half, EDGE_RAISED, 1373 BF_DIAGONAL_ENDBOTTOMLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST); 1374 1375 // If the button is pressed, draw hatching. 1376 if (extra.classic_state & DFCS_PUSHED) { 1377 SkPaint paint; 1378 SetCheckerboardShader(&paint, rect_win); 1379 1380 // Fill all three pieces with the pattern. 1381 canvas->drawIRect(skia::RECTToSkIRect(top_section), paint); 1382 1383 SkScalar left_triangle_top = SkIntToScalar(left_half.top); 1384 SkScalar left_triangle_right = SkIntToScalar(left_half.right); 1385 SkPath left_triangle; 1386 left_triangle.moveTo(SkIntToScalar(left_half.left), left_triangle_top); 1387 left_triangle.lineTo(left_triangle_right, left_triangle_top); 1388 left_triangle.lineTo(left_triangle_right, 1389 SkIntToScalar(left_half.bottom)); 1390 left_triangle.close(); 1391 canvas->drawPath(left_triangle, paint); 1392 1393 SkScalar right_triangle_left = SkIntToScalar(right_half.left); 1394 SkScalar right_triangle_top = SkIntToScalar(right_half.top); 1395 SkPath right_triangle; 1396 right_triangle.moveTo(right_triangle_left, right_triangle_top); 1397 right_triangle.lineTo(SkIntToScalar(right_half.right), 1398 right_triangle_top); 1399 right_triangle.lineTo(right_triangle_left, 1400 SkIntToScalar(right_half.bottom)); 1401 right_triangle.close(); 1402 canvas->drawPath(right_triangle, paint); 1403 } 1404 } 1405 return S_OK; 1406 } 1407 1408 HRESULT NativeThemeWin::PaintProgressBar( 1409 HDC hdc, 1410 const gfx::Rect& rect, 1411 const ProgressBarExtraParams& extra) const { 1412 // There is no documentation about the animation speed, frame-rate, nor 1413 // size of moving overlay of the indeterminate progress bar. 1414 // So we just observed real-world programs and guessed following parameters. 1415 const int kDeteminateOverlayPixelsPerSecond = 300; 1416 const int kDeteminateOverlayWidth = 120; 1417 const int kIndeterminateOverlayPixelsPerSecond = 175; 1418 const int kVistaIndeterminateOverlayWidth = 120; 1419 const int kXPIndeterminateOverlayWidth = 55; 1420 // The thickness of the bar frame inside |value_rect| 1421 const int kXPBarPadding = 3; 1422 1423 RECT bar_rect = rect.ToRECT(); 1424 RECT value_rect = gfx::Rect(extra.value_rect_x, 1425 extra.value_rect_y, 1426 extra.value_rect_width, 1427 extra.value_rect_height).ToRECT(); 1428 1429 bool pre_vista = base::win::GetVersion() < base::win::VERSION_VISTA; 1430 HANDLE handle = GetThemeHandle(PROGRESS); 1431 if (handle && draw_theme_ && draw_theme_ex_) { 1432 draw_theme_(handle, hdc, PP_BAR, 0, &bar_rect, NULL); 1433 1434 int bar_width = bar_rect.right - bar_rect.left; 1435 if (extra.determinate) { 1436 // TODO(morrita): this RTL guess can be wrong. 1437 // We should pass the direction from WebKit side. 1438 bool is_rtl = (bar_rect.right == value_rect.right && 1439 bar_rect.left != value_rect.left); 1440 // We should care the direction here because PP_CNUNK painting 1441 // is asymmetric. 1442 DTBGOPTS value_draw_options; 1443 value_draw_options.dwSize = sizeof(DTBGOPTS); 1444 value_draw_options.dwFlags = is_rtl ? DTBG_MIRRORDC : 0; 1445 value_draw_options.rcClip = bar_rect; 1446 1447 if (pre_vista) { 1448 // On XP, progress bar is chunk-style and has no glossy effect. 1449 // We need to shrink destination rect to fit the part inside the bar 1450 // with an appropriate margin. 1451 RECT shrunk_value_rect = InsetRect(&value_rect, kXPBarPadding); 1452 draw_theme_ex_(handle, hdc, PP_CHUNK, 0, 1453 &shrunk_value_rect, &value_draw_options); 1454 } else { 1455 // On Vista or later, the progress bar part has a 1456 // single-block value part. It also has glossy effect. 1457 // And the value part has exactly same height as the bar part 1458 // so we don't need to shrink the rect. 1459 draw_theme_ex_(handle, hdc, PP_FILL, 0, 1460 &value_rect, &value_draw_options); 1461 1462 int dx = ComputeAnimationProgress(bar_width, 1463 kDeteminateOverlayWidth, 1464 kDeteminateOverlayPixelsPerSecond, 1465 extra.animated_seconds); 1466 RECT overlay_rect = value_rect; 1467 overlay_rect.left += dx; 1468 overlay_rect.right = overlay_rect.left + kDeteminateOverlayWidth; 1469 draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &value_rect); 1470 } 1471 } else { 1472 // A glossy overlay for indeterminate progress bar has small pause 1473 // after each animation. We emulate this by adding an invisible margin 1474 // the animation has to traverse. 1475 int width_with_margin = bar_width + kIndeterminateOverlayPixelsPerSecond; 1476 int overlay_width = pre_vista ? 1477 kXPIndeterminateOverlayWidth : kVistaIndeterminateOverlayWidth; 1478 int dx = ComputeAnimationProgress(width_with_margin, 1479 overlay_width, 1480 kIndeterminateOverlayPixelsPerSecond, 1481 extra.animated_seconds); 1482 RECT overlay_rect = bar_rect; 1483 overlay_rect.left += dx; 1484 overlay_rect.right = overlay_rect.left + overlay_width; 1485 if (pre_vista) { 1486 RECT shrunk_rect = InsetRect(&overlay_rect, kXPBarPadding); 1487 RECT shrunk_bar_rect = InsetRect(&bar_rect, kXPBarPadding); 1488 draw_theme_(handle, hdc, PP_CHUNK, 0, &shrunk_rect, &shrunk_bar_rect); 1489 } else { 1490 draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &bar_rect); 1491 } 1492 } 1493 1494 return S_OK; 1495 } 1496 1497 HBRUSH bg_brush = GetSysColorBrush(COLOR_BTNFACE); 1498 HBRUSH fg_brush = GetSysColorBrush(COLOR_BTNSHADOW); 1499 FillRect(hdc, &bar_rect, bg_brush); 1500 FillRect(hdc, &value_rect, fg_brush); 1501 DrawEdge(hdc, &bar_rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); 1502 return S_OK; 1503 } 1504 1505 HRESULT NativeThemeWin::PaintWindowResizeGripper(HDC hdc, 1506 const gfx::Rect& rect) const { 1507 HANDLE handle = GetThemeHandle(STATUS); 1508 RECT rect_win = rect.ToRECT(); 1509 if (handle && draw_theme_) { 1510 // Paint the status bar gripper. There doesn't seem to be a 1511 // standard gripper in Windows for the space between 1512 // scrollbars. This is pretty close, but it's supposed to be 1513 // painted over a status bar. 1514 return draw_theme_(handle, hdc, SP_GRIPPER, 0, &rect_win, NULL); 1515 } 1516 1517 // Draw a windows classic scrollbar gripper. 1518 DrawFrameControl(hdc, &rect_win, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); 1519 return S_OK; 1520 } 1521 1522 HRESULT NativeThemeWin::PaintTabPanelBackground(HDC hdc, 1523 const gfx::Rect& rect) const { 1524 HANDLE handle = GetThemeHandle(TAB); 1525 RECT rect_win = rect.ToRECT(); 1526 if (handle && draw_theme_) 1527 return draw_theme_(handle, hdc, TABP_BODY, 0, &rect_win, NULL); 1528 1529 // Classic just renders a flat color background. 1530 FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1)); 1531 return S_OK; 1532 } 1533 1534 HRESULT NativeThemeWin::PaintTextField( 1535 HDC hdc, 1536 Part part, 1537 State state, 1538 const gfx::Rect& rect, 1539 const TextFieldExtraParams& extra) const { 1540 int part_id = EP_EDITTEXT; 1541 int state_id = ETS_NORMAL; 1542 switch (state) { 1543 case kNormal: 1544 if (extra.is_read_only) { 1545 state_id = ETS_READONLY; 1546 } else if (extra.is_focused) { 1547 state_id = ETS_FOCUSED; 1548 } else { 1549 state_id = ETS_NORMAL; 1550 } 1551 break; 1552 case kHovered: 1553 state_id = ETS_HOT; 1554 break; 1555 case kPressed: 1556 state_id = ETS_SELECTED; 1557 break; 1558 case kDisabled: 1559 state_id = ETS_DISABLED; 1560 break; 1561 default: 1562 NOTREACHED() << "Invalid state: " << state; 1563 break; 1564 } 1565 1566 RECT rect_win = rect.ToRECT(); 1567 return PaintTextField(hdc, part_id, state_id, extra.classic_state, 1568 &rect_win, 1569 skia::SkColorToCOLORREF(extra.background_color), 1570 extra.fill_content_area, extra.draw_edges); 1571 } 1572 1573 HRESULT NativeThemeWin::PaintTextField(HDC hdc, 1574 int part_id, 1575 int state_id, 1576 int classic_state, 1577 RECT* rect, 1578 COLORREF color, 1579 bool fill_content_area, 1580 bool draw_edges) const { 1581 // TODO(ojan): http://b/1210017 Figure out how to give the ability to 1582 // exclude individual edges from being drawn. 1583 1584 HANDLE handle = GetThemeHandle(TEXTFIELD); 1585 // TODO(mpcomplete): can we detect if the color is specified by the user, 1586 // and if not, just use the system color? 1587 // CreateSolidBrush() accepts a RGB value but alpha must be 0. 1588 HBRUSH bg_brush = CreateSolidBrush(color); 1589 HRESULT hr; 1590 // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible 1591 // draw_theme_ex_ is NULL and draw_theme_ is non-null. 1592 if (handle && (draw_theme_ex_ || (draw_theme_ && draw_edges))) { 1593 if (draw_theme_ex_) { 1594 static const DTBGOPTS omit_border_options = { 1595 sizeof(DTBGOPTS), 1596 DTBG_OMITBORDER, 1597 { 0, 0, 0, 0 } 1598 }; 1599 const DTBGOPTS* draw_opts = draw_edges ? NULL : &omit_border_options; 1600 hr = draw_theme_ex_(handle, hdc, part_id, state_id, rect, draw_opts); 1601 } else { 1602 hr = draw_theme_(handle, hdc, part_id, state_id, rect, NULL); 1603 } 1604 1605 // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL. 1606 if (fill_content_area && get_theme_content_rect_) { 1607 RECT content_rect; 1608 hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect, 1609 &content_rect); 1610 FillRect(hdc, &content_rect, bg_brush); 1611 } 1612 } else { 1613 // Draw it manually. 1614 if (draw_edges) 1615 DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); 1616 1617 if (fill_content_area) { 1618 FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ? 1619 reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1) : bg_brush); 1620 } 1621 hr = S_OK; 1622 } 1623 DeleteObject(bg_brush); 1624 return hr; 1625 } 1626 1627 HRESULT NativeThemeWin::PaintScaledTheme(HANDLE theme, 1628 HDC hdc, 1629 int part_id, 1630 int state_id, 1631 const gfx::Rect& rect) const { 1632 // Correct the scaling and positioning of sub-components such as scrollbar 1633 // arrows and thumb grippers in the event that the world transform applies 1634 // scaling (e.g. in high-DPI mode). 1635 XFORM save_transform; 1636 if (GetWorldTransform(hdc, &save_transform)) { 1637 float scale = save_transform.eM11; 1638 if (scale != 1 && save_transform.eM12 == 0) { 1639 ModifyWorldTransform(hdc, NULL, MWT_IDENTITY); 1640 gfx::Rect scaled_rect = gfx::ToEnclosedRect( 1641 gfx::ScaleRect(rect, scale)); 1642 RECT bounds = gfx::Rect(scaled_rect.x() + save_transform.eDx, 1643 scaled_rect.y() + save_transform.eDy, 1644 scaled_rect.width(), 1645 scaled_rect.height()).ToRECT(); 1646 HRESULT result = draw_theme_(theme, hdc, part_id, state_id, &bounds, 1647 NULL); 1648 SetWorldTransform(hdc, &save_transform); 1649 return result; 1650 } 1651 } 1652 RECT bounds = rect.ToRECT(); 1653 return draw_theme_(theme, hdc, part_id, state_id, &bounds, NULL); 1654 } 1655 1656 // static 1657 NativeThemeWin::ThemeName NativeThemeWin::GetThemeName(Part part) { 1658 ThemeName name; 1659 switch (part) { 1660 case kCheckbox: 1661 case kRadio: 1662 case kPushButton: 1663 name = BUTTON; 1664 break; 1665 case kInnerSpinButton: 1666 name = SPIN; 1667 break; 1668 case kMenuCheck: 1669 case kMenuPopupGutter: 1670 case kMenuList: 1671 case kMenuPopupArrow: 1672 case kMenuPopupSeparator: 1673 name = MENU; 1674 break; 1675 case kProgressBar: 1676 name = PROGRESS; 1677 break; 1678 case kScrollbarDownArrow: 1679 case kScrollbarLeftArrow: 1680 case kScrollbarRightArrow: 1681 case kScrollbarUpArrow: 1682 case kScrollbarHorizontalThumb: 1683 case kScrollbarVerticalThumb: 1684 case kScrollbarHorizontalTrack: 1685 case kScrollbarVerticalTrack: 1686 name = SCROLLBAR; 1687 break; 1688 case kSliderTrack: 1689 case kSliderThumb: 1690 name = TRACKBAR; 1691 break; 1692 case kTextField: 1693 name = TEXTFIELD; 1694 break; 1695 case kWindowResizeGripper: 1696 name = STATUS; 1697 break; 1698 default: 1699 NOTREACHED() << "Invalid part: " << part; 1700 break; 1701 } 1702 return name; 1703 } 1704 1705 // static 1706 int NativeThemeWin::GetWindowsPart(Part part, 1707 State state, 1708 const ExtraParams& extra) { 1709 int part_id; 1710 switch (part) { 1711 case kCheckbox: 1712 part_id = BP_CHECKBOX; 1713 break; 1714 case kMenuCheck: 1715 part_id = MENU_POPUPCHECK; 1716 break; 1717 case kMenuPopupArrow: 1718 part_id = MENU_POPUPSUBMENU; 1719 break; 1720 case kMenuPopupGutter: 1721 part_id = MENU_POPUPGUTTER; 1722 break; 1723 case kMenuPopupSeparator: 1724 part_id = MENU_POPUPSEPARATOR; 1725 break; 1726 case kPushButton: 1727 part_id = BP_PUSHBUTTON; 1728 break; 1729 case kRadio: 1730 part_id = BP_RADIOBUTTON; 1731 break; 1732 case kWindowResizeGripper: 1733 part_id = SP_GRIPPER; 1734 break; 1735 case kScrollbarDownArrow: 1736 case kScrollbarLeftArrow: 1737 case kScrollbarRightArrow: 1738 case kScrollbarUpArrow: 1739 part_id = SBP_ARROWBTN; 1740 break; 1741 case kScrollbarHorizontalThumb: 1742 part_id = SBP_THUMBBTNHORZ; 1743 break; 1744 case kScrollbarVerticalThumb: 1745 part_id = SBP_THUMBBTNVERT; 1746 break; 1747 default: 1748 NOTREACHED() << "Invalid part: " << part; 1749 break; 1750 } 1751 return part_id; 1752 } 1753 1754 int NativeThemeWin::GetWindowsState(Part part, 1755 State state, 1756 const ExtraParams& extra) { 1757 int state_id; 1758 switch (part) { 1759 case kCheckbox: 1760 switch (state) { 1761 case kNormal: 1762 state_id = CBS_UNCHECKEDNORMAL; 1763 break; 1764 case kHovered: 1765 state_id = CBS_UNCHECKEDHOT; 1766 break; 1767 case kPressed: 1768 state_id = CBS_UNCHECKEDPRESSED; 1769 break; 1770 case kDisabled: 1771 state_id = CBS_UNCHECKEDDISABLED; 1772 break; 1773 default: 1774 NOTREACHED() << "Invalid state: " << state; 1775 break; 1776 } 1777 break; 1778 case kMenuCheck: 1779 switch (state) { 1780 case kNormal: 1781 case kHovered: 1782 case kPressed: 1783 state_id = extra.menu_check.is_radio ? MC_BULLETNORMAL 1784 : MC_CHECKMARKNORMAL; 1785 break; 1786 case kDisabled: 1787 state_id = extra.menu_check.is_radio ? MC_BULLETDISABLED 1788 : MC_CHECKMARKDISABLED; 1789 break; 1790 default: 1791 NOTREACHED() << "Invalid state: " << state; 1792 break; 1793 } 1794 break; 1795 case kMenuPopupArrow: 1796 case kMenuPopupGutter: 1797 case kMenuPopupSeparator: 1798 switch (state) { 1799 case kNormal: 1800 state_id = MBI_NORMAL; 1801 break; 1802 case kHovered: 1803 state_id = MBI_HOT; 1804 break; 1805 case kPressed: 1806 state_id = MBI_PUSHED; 1807 break; 1808 case kDisabled: 1809 state_id = MBI_DISABLED; 1810 break; 1811 default: 1812 NOTREACHED() << "Invalid state: " << state; 1813 break; 1814 } 1815 break; 1816 case kPushButton: 1817 switch (state) { 1818 case kNormal: 1819 state_id = PBS_NORMAL; 1820 break; 1821 case kHovered: 1822 state_id = PBS_HOT; 1823 break; 1824 case kPressed: 1825 state_id = PBS_PRESSED; 1826 break; 1827 case kDisabled: 1828 state_id = PBS_DISABLED; 1829 break; 1830 default: 1831 NOTREACHED() << "Invalid state: " << state; 1832 break; 1833 } 1834 break; 1835 case kRadio: 1836 switch (state) { 1837 case kNormal: 1838 state_id = RBS_UNCHECKEDNORMAL; 1839 break; 1840 case kHovered: 1841 state_id = RBS_UNCHECKEDHOT; 1842 break; 1843 case kPressed: 1844 state_id = RBS_UNCHECKEDPRESSED; 1845 break; 1846 case kDisabled: 1847 state_id = RBS_UNCHECKEDDISABLED; 1848 break; 1849 default: 1850 NOTREACHED() << "Invalid state: " << state; 1851 break; 1852 } 1853 break; 1854 case kWindowResizeGripper: 1855 switch (state) { 1856 case kNormal: 1857 case kHovered: 1858 case kPressed: 1859 case kDisabled: 1860 state_id = 1; // gripper has no windows state 1861 break; 1862 default: 1863 NOTREACHED() << "Invalid state: " << state; 1864 break; 1865 } 1866 break; 1867 case kScrollbarDownArrow: 1868 switch (state) { 1869 case kNormal: 1870 state_id = ABS_DOWNNORMAL; 1871 break; 1872 case kHovered: 1873 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit. 1874 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ? 1875 ABS_DOWNHOT : ABS_DOWNHOVER; 1876 break; 1877 case kPressed: 1878 state_id = ABS_DOWNPRESSED; 1879 break; 1880 case kDisabled: 1881 state_id = ABS_DOWNDISABLED; 1882 break; 1883 default: 1884 NOTREACHED() << "Invalid state: " << state; 1885 break; 1886 } 1887 break; 1888 case kScrollbarLeftArrow: 1889 switch (state) { 1890 case kNormal: 1891 state_id = ABS_LEFTNORMAL; 1892 break; 1893 case kHovered: 1894 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit. 1895 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ? 1896 ABS_LEFTHOT : ABS_LEFTHOVER; 1897 break; 1898 case kPressed: 1899 state_id = ABS_LEFTPRESSED; 1900 break; 1901 case kDisabled: 1902 state_id = ABS_LEFTDISABLED; 1903 break; 1904 default: 1905 NOTREACHED() << "Invalid state: " << state; 1906 break; 1907 } 1908 break; 1909 case kScrollbarRightArrow: 1910 switch (state) { 1911 case kNormal: 1912 state_id = ABS_RIGHTNORMAL; 1913 break; 1914 case kHovered: 1915 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit. 1916 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ? 1917 ABS_RIGHTHOT : ABS_RIGHTHOVER; 1918 break; 1919 case kPressed: 1920 state_id = ABS_RIGHTPRESSED; 1921 break; 1922 case kDisabled: 1923 state_id = ABS_RIGHTDISABLED; 1924 break; 1925 default: 1926 NOTREACHED() << "Invalid state: " << state; 1927 break; 1928 } 1929 break; 1930 case kScrollbarUpArrow: 1931 switch (state) { 1932 case kNormal: 1933 state_id = ABS_UPNORMAL; 1934 break; 1935 case kHovered: 1936 // Mimic ScrollbarThemeChromiumWin.cpp in WebKit. 1937 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ? 1938 ABS_UPHOT : ABS_UPHOVER; 1939 break; 1940 case kPressed: 1941 state_id = ABS_UPPRESSED; 1942 break; 1943 case kDisabled: 1944 state_id = ABS_UPDISABLED; 1945 break; 1946 default: 1947 NOTREACHED() << "Invalid state: " << state; 1948 break; 1949 } 1950 break; 1951 case kScrollbarHorizontalThumb: 1952 case kScrollbarVerticalThumb: 1953 switch (state) { 1954 case kNormal: 1955 state_id = SCRBS_NORMAL; 1956 break; 1957 case kHovered: 1958 // Mimic WebKit's behaviour in ScrollbarThemeChromiumWin.cpp. 1959 state_id = base::win::GetVersion() < base::win::VERSION_VISTA ? 1960 SCRBS_HOT : SCRBS_HOVER; 1961 break; 1962 case kPressed: 1963 state_id = SCRBS_PRESSED; 1964 break; 1965 case kDisabled: 1966 state_id = SCRBS_DISABLED; 1967 break; 1968 default: 1969 NOTREACHED() << "Invalid state: " << state; 1970 break; 1971 } 1972 break; 1973 default: 1974 NOTREACHED() << "Invalid part: " << part; 1975 break; 1976 } 1977 return state_id; 1978 } 1979 1980 HRESULT NativeThemeWin::GetThemeInt(ThemeName theme, 1981 int part_id, 1982 int state_id, 1983 int prop_id, 1984 int *value) const { 1985 HANDLE handle = GetThemeHandle(theme); 1986 if (handle && get_theme_int_) 1987 return get_theme_int_(handle, part_id, state_id, prop_id, value); 1988 return E_NOTIMPL; 1989 } 1990 1991 HRESULT NativeThemeWin::PaintFrameControl(HDC hdc, 1992 const gfx::Rect& rect, 1993 UINT type, 1994 UINT state, 1995 bool is_selected, 1996 State control_state) const { 1997 const int width = rect.width(); 1998 const int height = rect.height(); 1999 2000 // DrawFrameControl for menu arrow/check wants a monochrome bitmap. 2001 base::win::ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL)); 2002 2003 if (mask_bitmap == NULL) 2004 return E_OUTOFMEMORY; 2005 2006 base::win::ScopedCreateDC bitmap_dc(CreateCompatibleDC(NULL)); 2007 base::win::ScopedSelectObject select_bitmap(bitmap_dc, mask_bitmap); 2008 RECT local_rect = { 0, 0, width, height }; 2009 DrawFrameControl(bitmap_dc, &local_rect, type, state); 2010 2011 // We're going to use BitBlt with a b&w mask. This results in using the dest 2012 // dc's text color for the black bits in the mask, and the dest dc's 2013 // background color for the white bits in the mask. DrawFrameControl draws the 2014 // check in black, and the background in white. 2015 int bg_color_key; 2016 int text_color_key; 2017 switch (control_state) { 2018 case NativeTheme::kHovered: 2019 bg_color_key = COLOR_HIGHLIGHT; 2020 text_color_key = COLOR_HIGHLIGHTTEXT; 2021 break; 2022 case NativeTheme::kNormal: 2023 bg_color_key = COLOR_MENU; 2024 text_color_key = COLOR_MENUTEXT; 2025 break; 2026 case NativeTheme::kDisabled: 2027 bg_color_key = is_selected ? COLOR_HIGHLIGHT : COLOR_MENU; 2028 text_color_key = COLOR_GRAYTEXT; 2029 break; 2030 default: 2031 NOTREACHED(); 2032 bg_color_key = COLOR_MENU; 2033 text_color_key = COLOR_MENUTEXT; 2034 break; 2035 } 2036 COLORREF old_bg_color = SetBkColor(hdc, GetSysColor(bg_color_key)); 2037 COLORREF old_text_color = SetTextColor(hdc, GetSysColor(text_color_key)); 2038 BitBlt(hdc, rect.x(), rect.y(), width, height, bitmap_dc, 0, 0, SRCCOPY); 2039 SetBkColor(hdc, old_bg_color); 2040 SetTextColor(hdc, old_text_color); 2041 2042 return S_OK; 2043 } 2044 2045 HANDLE NativeThemeWin::GetThemeHandle(ThemeName theme_name) const { 2046 if (!open_theme_ || theme_name < 0 || theme_name >= LAST) 2047 return 0; 2048 2049 if (theme_handles_[theme_name]) 2050 return theme_handles_[theme_name]; 2051 2052 // Not found, try to load it. 2053 HANDLE handle = 0; 2054 switch (theme_name) { 2055 case BUTTON: 2056 handle = open_theme_(NULL, L"Button"); 2057 break; 2058 case LIST: 2059 handle = open_theme_(NULL, L"Listview"); 2060 break; 2061 case MENU: 2062 handle = open_theme_(NULL, L"Menu"); 2063 break; 2064 case MENULIST: 2065 handle = open_theme_(NULL, L"Combobox"); 2066 break; 2067 case SCROLLBAR: 2068 handle = open_theme_(NULL, L"Scrollbar"); 2069 break; 2070 case STATUS: 2071 handle = open_theme_(NULL, L"Status"); 2072 break; 2073 case TAB: 2074 handle = open_theme_(NULL, L"Tab"); 2075 break; 2076 case TEXTFIELD: 2077 handle = open_theme_(NULL, L"Edit"); 2078 break; 2079 case TRACKBAR: 2080 handle = open_theme_(NULL, L"Trackbar"); 2081 break; 2082 case WINDOW: 2083 handle = open_theme_(NULL, L"Window"); 2084 break; 2085 case PROGRESS: 2086 handle = open_theme_(NULL, L"Progress"); 2087 break; 2088 case SPIN: 2089 handle = open_theme_(NULL, L"Spin"); 2090 break; 2091 default: 2092 NOTREACHED(); 2093 } 2094 theme_handles_[theme_name] = handle; 2095 return handle; 2096 } 2097 2098 } // namespace ui 2099