Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(176)

Side by Side Diff: ui/base/native_theme/native_theme_win.cc

Issue 10387121: Revert 136996 - ui: Move NativeTheme files into ui/base/native_theme/ directory. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW
« no previous file with comments | « ui/base/native_theme/native_theme_win.h ('k') | ui/base/native_theme/native_theme_win_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698