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