OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2008 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 "base/gfx/native_theme.h" | |
6 | |
7 #include <windows.h> | |
8 #include <uxtheme.h> | |
9 #include <vsstyle.h> | |
10 #include <vssym32.h> | |
11 | |
12 #include "base/gfx/gdi_util.h" | |
13 #include "base/gfx/rect.h" | |
14 #include "base/logging.h" | |
15 #include "base/scoped_handle.h" | |
16 #include "skia/ext/platform_canvas.h" | |
17 #include "skia/ext/skia_utils_win.h" | |
18 #include "third_party/skia/include/core/SkShader.h" | |
19 | |
20 namespace { | |
21 | |
22 void SetCheckerboardShader(SkPaint* paint, const RECT& align_rect) { | |
23 // Create a 2x2 checkerboard pattern using the 3D face and highlight colors. | |
24 SkColor face = skia::COLORREFToSkColor(GetSysColor(COLOR_3DFACE)); | |
25 SkColor highlight = skia::COLORREFToSkColor(GetSysColor(COLOR_3DHILIGHT)); | |
26 SkColor buffer[] = { face, highlight, highlight, face }; | |
27 // Confusing bit: we first create a temporary bitmap with our desired pattern, | |
28 // then copy it to another bitmap. The temporary bitmap doesn't take | |
29 // ownership of the pixel data, and so will point to garbage when this | |
30 // function returns. The copy will copy the pixel data into a place owned by | |
31 // the bitmap, which is in turn owned by the shader, etc., so it will live | |
32 // until we're done using it. | |
33 SkBitmap temp_bitmap; | |
34 temp_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2); | |
35 temp_bitmap.setPixels(buffer); | |
36 SkBitmap bitmap; | |
37 temp_bitmap.copyTo(&bitmap, temp_bitmap.config()); | |
38 SkShader* shader = SkShader::CreateBitmapShader(bitmap, | |
39 SkShader::kRepeat_TileMode, | |
40 SkShader::kRepeat_TileMode); | |
41 | |
42 // Align the pattern with the upper corner of |align_rect|. | |
43 SkMatrix matrix; | |
44 matrix.setTranslate(SkIntToScalar(align_rect.left), | |
45 SkIntToScalar(align_rect.top)); | |
46 shader->setLocalMatrix(matrix); | |
47 paint->setShader(shader)->safeUnref(); | |
48 } | |
49 | |
50 } // namespace | |
51 | |
52 namespace gfx { | |
53 | |
54 /* static */ | |
55 const NativeTheme* NativeTheme::instance() { | |
56 // The global NativeTheme instance. | |
57 static const NativeTheme s_native_theme; | |
58 return &s_native_theme; | |
59 } | |
60 | |
61 NativeTheme::NativeTheme() | |
62 : theme_dll_(LoadLibrary(L"uxtheme.dll")), | |
63 draw_theme_(NULL), | |
64 draw_theme_ex_(NULL), | |
65 get_theme_color_(NULL), | |
66 get_theme_content_rect_(NULL), | |
67 get_theme_part_size_(NULL), | |
68 open_theme_(NULL), | |
69 close_theme_(NULL), | |
70 set_theme_properties_(NULL), | |
71 is_theme_active_(NULL), | |
72 get_theme_int_(NULL) { | |
73 if (theme_dll_) { | |
74 draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>( | |
75 GetProcAddress(theme_dll_, "DrawThemeBackground")); | |
76 draw_theme_ex_ = reinterpret_cast<DrawThemeBackgroundExPtr>( | |
77 GetProcAddress(theme_dll_, "DrawThemeBackgroundEx")); | |
78 get_theme_color_ = reinterpret_cast<GetThemeColorPtr>( | |
79 GetProcAddress(theme_dll_, "GetThemeColor")); | |
80 get_theme_content_rect_ = reinterpret_cast<GetThemeContentRectPtr>( | |
81 GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect")); | |
82 get_theme_part_size_ = reinterpret_cast<GetThemePartSizePtr>( | |
83 GetProcAddress(theme_dll_, "GetThemePartSize")); | |
84 open_theme_ = reinterpret_cast<OpenThemeDataPtr>( | |
85 GetProcAddress(theme_dll_, "OpenThemeData")); | |
86 close_theme_ = reinterpret_cast<CloseThemeDataPtr>( | |
87 GetProcAddress(theme_dll_, "CloseThemeData")); | |
88 set_theme_properties_ = reinterpret_cast<SetThemeAppPropertiesPtr>( | |
89 GetProcAddress(theme_dll_, "SetThemeAppProperties")); | |
90 is_theme_active_ = reinterpret_cast<IsThemeActivePtr>( | |
91 GetProcAddress(theme_dll_, "IsThemeActive")); | |
92 get_theme_int_ = reinterpret_cast<GetThemeIntPtr>( | |
93 GetProcAddress(theme_dll_, "GetThemeInt")); | |
94 } | |
95 memset(theme_handles_, 0, sizeof(theme_handles_)); | |
96 } | |
97 | |
98 NativeTheme::~NativeTheme() { | |
99 if (theme_dll_) { | |
100 // todo (cpu): fix this soon. | |
101 // CloseHandles(); | |
102 FreeLibrary(theme_dll_); | |
103 } | |
104 } | |
105 | |
106 HRESULT NativeTheme::PaintButton(HDC hdc, | |
107 int part_id, | |
108 int state_id, | |
109 int classic_state, | |
110 RECT* rect) const { | |
111 HANDLE handle = GetThemeHandle(BUTTON); | |
112 if (handle && draw_theme_) | |
113 return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); | |
114 | |
115 // Draw it manually. | |
116 // All pressed states have both low bits set, and no other states do. | |
117 const bool focused = ((state_id & ETS_FOCUSED) == ETS_FOCUSED); | |
118 const bool pressed = ((state_id & PBS_PRESSED) == PBS_PRESSED); | |
119 if ((BP_PUSHBUTTON == part_id) && (pressed || focused)) { | |
120 // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the | |
121 // button itself is shrunk by 1 pixel. | |
122 HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW); | |
123 if (brush) { | |
124 FrameRect(hdc, rect, brush); | |
125 InflateRect(rect, -1, -1); | |
126 } | |
127 } | |
128 DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state); | |
129 | |
130 // Draw the focus rectangle (the dotted line box) only on buttons. For radio | |
131 // and checkboxes, we let webkit draw the focus rectangle (orange glow). | |
132 if ((BP_PUSHBUTTON == part_id) && focused) { | |
133 // The focus rect is inside the button. The exact number of pixels depends | |
134 // on whether we're in classic mode or using uxtheme. | |
135 if (handle && get_theme_content_rect_) { | |
136 get_theme_content_rect_(handle, hdc, part_id, state_id, rect, rect); | |
137 } else { | |
138 InflateRect(rect, -GetSystemMetrics(SM_CXEDGE), | |
139 -GetSystemMetrics(SM_CYEDGE)); | |
140 } | |
141 DrawFocusRect(hdc, rect); | |
142 } | |
143 | |
144 return S_OK; | |
145 } | |
146 | |
147 HRESULT NativeTheme::PaintDialogBackground(HDC hdc, bool active, | |
148 RECT* rect) const { | |
149 HANDLE handle = GetThemeHandle(WINDOW); | |
150 if (handle && draw_theme_) { | |
151 return draw_theme_(handle, hdc, WP_DIALOG, | |
152 active ? FS_ACTIVE : FS_INACTIVE, rect, NULL); | |
153 } | |
154 | |
155 // Classic just renders a flat color background. | |
156 FillRect(hdc, rect, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1)); | |
157 return S_OK; | |
158 } | |
159 | |
160 HRESULT NativeTheme::PaintListBackground(HDC hdc, | |
161 bool enabled, | |
162 RECT* rect) const { | |
163 HANDLE handle = GetThemeHandle(LIST); | |
164 if (handle && draw_theme_) | |
165 return draw_theme_(handle, hdc, 1, TS_NORMAL, rect, NULL); | |
166 | |
167 // Draw it manually. | |
168 HBRUSH bg_brush = GetSysColorBrush(COLOR_WINDOW); | |
169 FillRect(hdc, rect, bg_brush); | |
170 DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); | |
171 return S_OK; | |
172 } | |
173 | |
174 HRESULT NativeTheme::PaintMenuArrow(ThemeName theme, | |
175 HDC hdc, | |
176 int part_id, | |
177 int state_id, | |
178 RECT* rect, | |
179 MenuArrowDirection arrow_direction, | |
180 bool is_highlighted) const { | |
181 HANDLE handle = GetThemeHandle(MENU); | |
182 if (handle && draw_theme_) { | |
183 if (arrow_direction == RIGHT_POINTING_ARROW) { | |
184 return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); | |
185 } else { | |
186 // There is no way to tell the uxtheme API to draw a left pointing arrow; | |
187 // it doesn't have a flag equivalent to DFCS_MENUARROWRIGHT. But they | |
188 // are needed for RTL locales on Vista. So use a memory DC and mirror | |
189 // the region with GDI's StretchBlt. | |
190 Rect r(*rect); | |
191 ScopedHDC mem_dc(CreateCompatibleDC(hdc)); | |
192 ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(), | |
193 r.height())); | |
194 HGDIOBJ old_bitmap = SelectObject(mem_dc, mem_bitmap); | |
195 // Copy and horizontally mirror the background from hdc into mem_dc. Use | |
196 // a negative-width source rect, starting at the rightmost pixel. | |
197 StretchBlt(mem_dc, 0, 0, r.width(), r.height(), | |
198 hdc, r.right()-1, r.y(), -r.width(), r.height(), SRCCOPY); | |
199 // Draw the arrow. | |
200 RECT theme_rect = {0, 0, r.width(), r.height()}; | |
201 HRESULT result = draw_theme_(handle, mem_dc, part_id, | |
202 state_id, &theme_rect, NULL); | |
203 // Copy and mirror the result back into mem_dc. | |
204 StretchBlt(hdc, r.x(), r.y(), r.width(), r.height(), | |
205 mem_dc, r.width()-1, 0, -r.width(), r.height(), SRCCOPY); | |
206 SelectObject(mem_dc, old_bitmap); | |
207 return result; | |
208 } | |
209 } | |
210 | |
211 // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a | |
212 // left pointing arrow. This makes the following 'if' statement slightly | |
213 // counterintuitive. | |
214 UINT state; | |
215 if (arrow_direction == RIGHT_POINTING_ARROW) | |
216 state = DFCS_MENUARROW; | |
217 else | |
218 state = DFCS_MENUARROWRIGHT; | |
219 return PaintFrameControl(hdc, rect, DFC_MENU, state, is_highlighted); | |
220 } | |
221 | |
222 HRESULT NativeTheme::PaintMenuBackground(ThemeName theme, | |
223 HDC hdc, | |
224 int part_id, | |
225 int state_id, | |
226 RECT* rect) const { | |
227 HANDLE handle = GetThemeHandle(MENU); | |
228 if (handle && draw_theme_) { | |
229 HRESULT result = draw_theme_(handle, hdc, part_id, state_id, rect, NULL); | |
230 FrameRect(hdc, rect, GetSysColorBrush(COLOR_3DSHADOW)); | |
231 return result; | |
232 } | |
233 | |
234 FillRect(hdc, rect, GetSysColorBrush(COLOR_MENU)); | |
235 DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT); | |
236 return S_OK; | |
237 } | |
238 | |
239 HRESULT NativeTheme::PaintMenuCheckBackground(ThemeName theme, | |
240 HDC hdc, | |
241 int part_id, | |
242 int state_id, | |
243 RECT* rect) const { | |
244 HANDLE handle = GetThemeHandle(MENU); | |
245 if (handle && draw_theme_) | |
246 return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); | |
247 // Nothing to do for background. | |
248 return S_OK; | |
249 } | |
250 | |
251 HRESULT NativeTheme::PaintMenuCheck(ThemeName theme, | |
252 HDC hdc, | |
253 int part_id, | |
254 int state_id, | |
255 RECT* rect, | |
256 bool is_highlighted) const { | |
257 HANDLE handle = GetThemeHandle(MENU); | |
258 if (handle && draw_theme_) { | |
259 return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); | |
260 } | |
261 return PaintFrameControl(hdc, rect, DFC_MENU, DFCS_MENUCHECK, is_highlighted); | |
262 } | |
263 | |
264 HRESULT NativeTheme::PaintMenuGutter(HDC hdc, | |
265 int part_id, | |
266 int state_id, | |
267 RECT* rect) const { | |
268 HANDLE handle = GetThemeHandle(MENU); | |
269 if (handle && draw_theme_) | |
270 return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); | |
271 return E_NOTIMPL; | |
272 } | |
273 | |
274 HRESULT NativeTheme::PaintMenuItemBackground(ThemeName theme, | |
275 HDC hdc, | |
276 int part_id, | |
277 int state_id, | |
278 bool selected, | |
279 RECT* rect) const { | |
280 HANDLE handle = GetThemeHandle(MENU); | |
281 if (handle && draw_theme_) | |
282 return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); | |
283 if (selected) | |
284 FillRect(hdc, rect, GetSysColorBrush(COLOR_HIGHLIGHT)); | |
285 return S_OK; | |
286 } | |
287 | |
288 HRESULT NativeTheme::PaintMenuList(HDC hdc, | |
289 int part_id, | |
290 int state_id, | |
291 int classic_state, | |
292 RECT* rect) const { | |
293 HANDLE handle = GetThemeHandle(MENULIST); | |
294 if (handle && draw_theme_) | |
295 return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); | |
296 | |
297 // Draw it manually. | |
298 DrawFrameControl(hdc, rect, DFC_SCROLL, DFCS_SCROLLCOMBOBOX | classic_state); | |
299 return S_OK; | |
300 } | |
301 | |
302 HRESULT NativeTheme::PaintMenuSeparator(HDC hdc, | |
303 int part_id, | |
304 int state_id, | |
305 RECT* rect) const { | |
306 HANDLE handle = GetThemeHandle(MENU); | |
307 if (handle && draw_theme_) | |
308 return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); | |
309 DrawEdge(hdc, rect, EDGE_ETCHED, BF_TOP); | |
310 return S_OK; | |
311 } | |
312 | |
313 HRESULT NativeTheme::PaintScrollbarArrow(HDC hdc, | |
314 int state_id, | |
315 int classic_state, | |
316 RECT* rect) const { | |
317 HANDLE handle = GetThemeHandle(SCROLLBAR); | |
318 if (handle && draw_theme_) | |
319 return draw_theme_(handle, hdc, SBP_ARROWBTN, state_id, rect, NULL); | |
320 | |
321 // Draw it manually. | |
322 DrawFrameControl(hdc, rect, DFC_SCROLL, classic_state); | |
323 return S_OK; | |
324 } | |
325 | |
326 HRESULT NativeTheme::PaintScrollbarTrack( | |
327 HDC hdc, | |
328 int part_id, | |
329 int state_id, | |
330 int classic_state, | |
331 RECT* target_rect, | |
332 RECT* align_rect, | |
333 skia::PlatformCanvas* canvas) const { | |
334 HANDLE handle = GetThemeHandle(SCROLLBAR); | |
335 if (handle && draw_theme_) | |
336 return draw_theme_(handle, hdc, part_id, state_id, target_rect, NULL); | |
337 | |
338 // Draw it manually. | |
339 const DWORD colorScrollbar = GetSysColor(COLOR_SCROLLBAR); | |
340 const DWORD color3DFace = GetSysColor(COLOR_3DFACE); | |
341 if ((colorScrollbar != color3DFace) && | |
342 (colorScrollbar != GetSysColor(COLOR_WINDOW))) { | |
343 FillRect(hdc, target_rect, reinterpret_cast<HBRUSH>(COLOR_SCROLLBAR + 1)); | |
344 } else { | |
345 SkPaint paint; | |
346 SetCheckerboardShader(&paint, *align_rect); | |
347 canvas->drawIRect(skia::RECTToSkIRect(*target_rect), paint); | |
348 } | |
349 if (classic_state & DFCS_PUSHED) | |
350 InvertRect(hdc, target_rect); | |
351 return S_OK; | |
352 } | |
353 | |
354 HRESULT NativeTheme::PaintScrollbarThumb(HDC hdc, | |
355 int part_id, | |
356 int state_id, | |
357 int classic_state, | |
358 RECT* rect) const { | |
359 HANDLE handle = GetThemeHandle(SCROLLBAR); | |
360 if (handle && draw_theme_) | |
361 return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); | |
362 | |
363 // Draw it manually. | |
364 if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT)) | |
365 DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT | BF_MIDDLE); | |
366 // Classic mode doesn't have a gripper. | |
367 return S_OK; | |
368 } | |
369 | |
370 HRESULT NativeTheme::PaintStatusGripper(HDC hdc, | |
371 int part_id, | |
372 int state_id, | |
373 int classic_state, | |
374 RECT* rect) const { | |
375 HANDLE handle = GetThemeHandle(STATUS); | |
376 if (handle && draw_theme_) { | |
377 // Paint the status bar gripper. There doesn't seem to be a | |
378 // standard gripper in Windows for the space between | |
379 // scrollbars. This is pretty close, but it's supposed to be | |
380 // painted over a status bar. | |
381 return draw_theme_(handle, hdc, SP_GRIPPER, 0, rect, NULL); | |
382 } | |
383 | |
384 // Draw a windows classic scrollbar gripper. | |
385 DrawFrameControl(hdc, rect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); | |
386 return S_OK; | |
387 } | |
388 | |
389 HRESULT NativeTheme::PaintTabPanelBackground(HDC hdc, RECT* rect) const { | |
390 HANDLE handle = GetThemeHandle(TAB); | |
391 if (handle && draw_theme_) | |
392 return draw_theme_(handle, hdc, TABP_BODY, 0, rect, NULL); | |
393 | |
394 // Classic just renders a flat color background. | |
395 FillRect(hdc, rect, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1)); | |
396 return S_OK; | |
397 } | |
398 | |
399 HRESULT NativeTheme::PaintTrackbar(HDC hdc, | |
400 int part_id, | |
401 int state_id, | |
402 int classic_state, | |
403 RECT* rect, | |
404 skia::PlatformCanvas* canvas) const { | |
405 // Make the channel be 4 px thick in the center of the supplied rect. (4 px | |
406 // matches what XP does in various menus; GetThemePartSize() doesn't seem to | |
407 // return good values here.) | |
408 RECT channel_rect = *rect; | |
409 const int channel_thickness = 4; | |
410 if (part_id == TKP_TRACK) { | |
411 channel_rect.top += | |
412 ((channel_rect.bottom - channel_rect.top - channel_thickness) / 2); | |
413 channel_rect.bottom = channel_rect.top + channel_thickness; | |
414 } else if (part_id == TKP_TRACKVERT) { | |
415 channel_rect.left += | |
416 ((channel_rect.right - channel_rect.left - channel_thickness) / 2); | |
417 channel_rect.right = channel_rect.left + channel_thickness; | |
418 } // else this isn't actually a channel, so |channel_rect| == |rect|. | |
419 | |
420 HANDLE handle = GetThemeHandle(TRACKBAR); | |
421 if (handle && draw_theme_) | |
422 return draw_theme_(handle, hdc, part_id, state_id, &channel_rect, NULL); | |
423 | |
424 // Classic mode, draw it manually. | |
425 if ((part_id == TKP_TRACK) || (part_id == TKP_TRACKVERT)) { | |
426 DrawEdge(hdc, &channel_rect, EDGE_SUNKEN, BF_RECT); | |
427 } else if (part_id == TKP_THUMBVERT) { | |
428 DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE); | |
429 } else { | |
430 // Split rect into top and bottom pieces. | |
431 RECT top_section = *rect; | |
432 RECT bottom_section = *rect; | |
433 top_section.bottom -= ((bottom_section.right - bottom_section.left) / 2); | |
434 bottom_section.top = top_section.bottom; | |
435 DrawEdge(hdc, &top_section, EDGE_RAISED, | |
436 BF_LEFT | BF_TOP | BF_RIGHT | BF_SOFT | BF_MIDDLE | BF_ADJUST); | |
437 | |
438 // Split triangular piece into two diagonals. | |
439 RECT& left_half = bottom_section; | |
440 RECT right_half = bottom_section; | |
441 right_half.left += ((bottom_section.right - bottom_section.left) / 2); | |
442 left_half.right = right_half.left; | |
443 DrawEdge(hdc, &left_half, EDGE_RAISED, | |
444 BF_DIAGONAL_ENDTOPLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST); | |
445 DrawEdge(hdc, &right_half, EDGE_RAISED, | |
446 BF_DIAGONAL_ENDBOTTOMLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST); | |
447 | |
448 // If the button is pressed, draw hatching. | |
449 if (classic_state & DFCS_PUSHED) { | |
450 SkPaint paint; | |
451 SetCheckerboardShader(&paint, *rect); | |
452 | |
453 // Fill all three pieces with the pattern. | |
454 canvas->drawIRect(skia::RECTToSkIRect(top_section), paint); | |
455 | |
456 SkScalar left_triangle_top = SkIntToScalar(left_half.top); | |
457 SkScalar left_triangle_right = SkIntToScalar(left_half.right); | |
458 SkPath left_triangle; | |
459 left_triangle.moveTo(SkIntToScalar(left_half.left), left_triangle_top); | |
460 left_triangle.lineTo(left_triangle_right, left_triangle_top); | |
461 left_triangle.lineTo(left_triangle_right, | |
462 SkIntToScalar(left_half.bottom)); | |
463 left_triangle.close(); | |
464 canvas->drawPath(left_triangle, paint); | |
465 | |
466 SkScalar right_triangle_left = SkIntToScalar(right_half.left); | |
467 SkScalar right_triangle_top = SkIntToScalar(right_half.top); | |
468 SkPath right_triangle; | |
469 right_triangle.moveTo(right_triangle_left, right_triangle_top); | |
470 right_triangle.lineTo(SkIntToScalar(right_half.right), | |
471 right_triangle_top); | |
472 right_triangle.lineTo(right_triangle_left, | |
473 SkIntToScalar(right_half.bottom)); | |
474 right_triangle.close(); | |
475 canvas->drawPath(right_triangle, paint); | |
476 } | |
477 } | |
478 return S_OK; | |
479 } | |
480 | |
481 HRESULT NativeTheme::PaintTextField(HDC hdc, | |
482 int part_id, | |
483 int state_id, | |
484 int classic_state, | |
485 RECT* rect, | |
486 COLORREF color, | |
487 bool fill_content_area, | |
488 bool draw_edges) const { | |
489 // TODO(ojan): http://b/1210017 Figure out how to give the ability to | |
490 // exclude individual edges from being drawn. | |
491 | |
492 HANDLE handle = GetThemeHandle(TEXTFIELD); | |
493 // TODO(mpcomplete): can we detect if the color is specified by the user, | |
494 // and if not, just use the system color? | |
495 // CreateSolidBrush() accepts a RGB value but alpha must be 0. | |
496 HBRUSH bg_brush = CreateSolidBrush(color); | |
497 HRESULT hr; | |
498 // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible | |
499 // draw_theme_ex_ is NULL and draw_theme_ is non-null. | |
500 if (handle && (draw_theme_ex_ || (draw_theme_ && draw_edges))) { | |
501 if (draw_theme_ex_) { | |
502 static DTBGOPTS omit_border_options = { | |
503 sizeof(DTBGOPTS), | |
504 DTBG_OMITBORDER, | |
505 {0,0,0,0} | |
506 }; | |
507 DTBGOPTS* draw_opts = draw_edges ? NULL : &omit_border_options; | |
508 hr = draw_theme_ex_(handle, hdc, part_id, state_id, rect, draw_opts); | |
509 } else { | |
510 hr = draw_theme_(handle, hdc, part_id, state_id, rect, NULL); | |
511 } | |
512 | |
513 // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL. | |
514 if (fill_content_area && get_theme_content_rect_) { | |
515 RECT content_rect; | |
516 hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect, | |
517 &content_rect); | |
518 FillRect(hdc, &content_rect, bg_brush); | |
519 } | |
520 } else { | |
521 // Draw it manually. | |
522 if (draw_edges) | |
523 DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); | |
524 | |
525 if (fill_content_area) { | |
526 FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ? | |
527 reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1) : bg_brush); | |
528 } | |
529 hr = S_OK; | |
530 } | |
531 DeleteObject(bg_brush); | |
532 return hr; | |
533 } | |
534 | |
535 bool NativeTheme::IsThemingActive() const { | |
536 if (is_theme_active_) | |
537 return !!is_theme_active_(); | |
538 return false; | |
539 } | |
540 | |
541 HRESULT NativeTheme::GetThemePartSize(ThemeName theme_name, | |
542 HDC hdc, | |
543 int part_id, | |
544 int state_id, | |
545 RECT* rect, | |
546 int ts, | |
547 SIZE* size) const { | |
548 HANDLE handle = GetThemeHandle(theme_name); | |
549 if (handle && get_theme_part_size_) | |
550 return get_theme_part_size_(handle, hdc, part_id, state_id, rect, ts, size); | |
551 | |
552 return E_NOTIMPL; | |
553 } | |
554 | |
555 HRESULT NativeTheme::GetThemeColor(ThemeName theme, | |
556 int part_id, | |
557 int state_id, | |
558 int prop_id, | |
559 SkColor* color) const { | |
560 HANDLE handle = GetThemeHandle(theme); | |
561 if (handle && get_theme_color_) { | |
562 COLORREF color_ref; | |
563 if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) == | |
564 S_OK) { | |
565 *color = skia::COLORREFToSkColor(color_ref); | |
566 return S_OK; | |
567 } | |
568 } | |
569 return E_NOTIMPL; | |
570 } | |
571 | |
572 SkColor NativeTheme::GetThemeColorWithDefault(ThemeName theme, | |
573 int part_id, | |
574 int state_id, | |
575 int prop_id, | |
576 int default_sys_color) const { | |
577 SkColor color; | |
578 if (GetThemeColor(theme, part_id, state_id, prop_id, &color) != S_OK) | |
579 color = skia::COLORREFToSkColor(GetSysColor(default_sys_color)); | |
580 return color; | |
581 } | |
582 | |
583 HRESULT NativeTheme::GetThemeInt(ThemeName theme, | |
584 int part_id, | |
585 int state_id, | |
586 int prop_id, | |
587 int *value) const { | |
588 HANDLE handle = GetThemeHandle(theme); | |
589 if (handle && get_theme_int_) | |
590 return get_theme_int_(handle, part_id, state_id, prop_id, value); | |
591 return E_NOTIMPL; | |
592 } | |
593 | |
594 Size NativeTheme::GetThemeBorderSize(ThemeName theme) const { | |
595 // For simplicity use the wildcard state==0, part==0, since it works | |
596 // for the cases we currently depend on. | |
597 int border; | |
598 if (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK) | |
599 return Size(border, border); | |
600 else | |
601 return Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE)); | |
602 } | |
603 | |
604 | |
605 void NativeTheme::DisableTheming() const { | |
606 if (!set_theme_properties_) | |
607 return; | |
608 set_theme_properties_(0); | |
609 } | |
610 | |
611 HRESULT NativeTheme::PaintFrameControl(HDC hdc, | |
612 RECT* rect, | |
613 UINT type, | |
614 UINT state, | |
615 bool is_highlighted) const { | |
616 const int width = rect->right - rect->left; | |
617 const int height = rect->bottom - rect->top; | |
618 | |
619 // DrawFrameControl for menu arrow/check wants a monochrome bitmap. | |
620 ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL)); | |
621 | |
622 if (mask_bitmap == NULL) | |
623 return E_OUTOFMEMORY; | |
624 | |
625 ScopedHDC bitmap_dc(CreateCompatibleDC(NULL)); | |
626 HGDIOBJ org_bitmap = SelectObject(bitmap_dc, mask_bitmap); | |
627 RECT local_rect = { 0, 0, width, height }; | |
628 DrawFrameControl(bitmap_dc, &local_rect, type, state); | |
629 | |
630 // We're going to use BitBlt with a b&w mask. This results in using the dest | |
631 // dc's text color for the black bits in the mask, and the dest dc's | |
632 // background color for the white bits in the mask. DrawFrameControl draws the | |
633 // check in black, and the background in white. | |
634 COLORREF old_bg_color = | |
635 SetBkColor(hdc, | |
636 GetSysColor(is_highlighted ? COLOR_HIGHLIGHT : COLOR_MENU)); | |
637 COLORREF old_text_color = | |
638 SetTextColor(hdc, | |
639 GetSysColor(is_highlighted ? COLOR_HIGHLIGHTTEXT : | |
640 COLOR_MENUTEXT)); | |
641 BitBlt(hdc, rect->left, rect->top, width, height, bitmap_dc, 0, 0, SRCCOPY); | |
642 SetBkColor(hdc, old_bg_color); | |
643 SetTextColor(hdc, old_text_color); | |
644 | |
645 SelectObject(bitmap_dc, org_bitmap); | |
646 | |
647 return S_OK; | |
648 } | |
649 | |
650 void NativeTheme::CloseHandles() const | |
651 { | |
652 if (!close_theme_) | |
653 return; | |
654 | |
655 for (int i = 0; i < LAST; ++i) { | |
656 if (theme_handles_[i]) | |
657 close_theme_(theme_handles_[i]); | |
658 theme_handles_[i] = NULL; | |
659 } | |
660 } | |
661 | |
662 HANDLE NativeTheme::GetThemeHandle(ThemeName theme_name) const | |
663 { | |
664 if (!open_theme_ || theme_name < 0 || theme_name >= LAST) | |
665 return 0; | |
666 | |
667 if (theme_handles_[theme_name]) | |
668 return theme_handles_[theme_name]; | |
669 | |
670 // Not found, try to load it. | |
671 HANDLE handle = 0; | |
672 switch (theme_name) { | |
673 case BUTTON: | |
674 handle = open_theme_(NULL, L"Button"); | |
675 break; | |
676 case LIST: | |
677 handle = open_theme_(NULL, L"Listview"); | |
678 break; | |
679 case MENU: | |
680 handle = open_theme_(NULL, L"Menu"); | |
681 break; | |
682 case MENULIST: | |
683 handle = open_theme_(NULL, L"Combobox"); | |
684 break; | |
685 case SCROLLBAR: | |
686 handle = open_theme_(NULL, L"Scrollbar"); | |
687 break; | |
688 case STATUS: | |
689 handle = open_theme_(NULL, L"Status"); | |
690 break; | |
691 case TAB: | |
692 handle = open_theme_(NULL, L"Tab"); | |
693 break; | |
694 case TEXTFIELD: | |
695 handle = open_theme_(NULL, L"Edit"); | |
696 break; | |
697 case TRACKBAR: | |
698 handle = open_theme_(NULL, L"Trackbar"); | |
699 break; | |
700 case WINDOW: | |
701 handle = open_theme_(NULL, L"Window"); | |
702 break; | |
703 default: | |
704 NOTREACHED(); | |
705 } | |
706 theme_handles_[theme_name] = handle; | |
707 return handle; | |
708 } | |
709 | |
710 } // namespace gfx | |
OLD | NEW |