|
1 /* vim: se cin sw=2 ts=2 et : */ |
|
2 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
3 * |
|
4 * This Source Code Form is subject to the terms of the Mozilla Public |
|
5 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
7 |
|
8 #include "mozilla/ArrayUtils.h" |
|
9 #include "mozilla/WindowsVersion.h" |
|
10 |
|
11 #include "nsUXThemeData.h" |
|
12 #include "nsDebug.h" |
|
13 #include "nsToolkit.h" |
|
14 #include "nsUXThemeConstants.h" |
|
15 |
|
16 using namespace mozilla; |
|
17 using namespace mozilla::widget; |
|
18 |
|
19 const wchar_t |
|
20 nsUXThemeData::kThemeLibraryName[] = L"uxtheme.dll"; |
|
21 |
|
22 HANDLE |
|
23 nsUXThemeData::sThemes[eUXNumClasses]; |
|
24 |
|
25 HMODULE |
|
26 nsUXThemeData::sThemeDLL = nullptr; |
|
27 |
|
28 bool |
|
29 nsUXThemeData::sFlatMenus = false; |
|
30 |
|
31 bool nsUXThemeData::sTitlebarInfoPopulatedAero = false; |
|
32 bool nsUXThemeData::sTitlebarInfoPopulatedThemed = false; |
|
33 SIZE nsUXThemeData::sCommandButtons[4]; |
|
34 |
|
35 void |
|
36 nsUXThemeData::Teardown() { |
|
37 Invalidate(); |
|
38 if(sThemeDLL) |
|
39 FreeLibrary(sThemeDLL); |
|
40 } |
|
41 |
|
42 void |
|
43 nsUXThemeData::Initialize() |
|
44 { |
|
45 ::ZeroMemory(sThemes, sizeof(sThemes)); |
|
46 NS_ASSERTION(!sThemeDLL, "nsUXThemeData being initialized twice!"); |
|
47 |
|
48 CheckForCompositor(true); |
|
49 Invalidate(); |
|
50 } |
|
51 |
|
52 void |
|
53 nsUXThemeData::Invalidate() { |
|
54 for(int i = 0; i < eUXNumClasses; i++) { |
|
55 if(sThemes[i]) { |
|
56 CloseThemeData(sThemes[i]); |
|
57 sThemes[i] = nullptr; |
|
58 } |
|
59 } |
|
60 BOOL useFlat = FALSE; |
|
61 sFlatMenus = ::SystemParametersInfo(SPI_GETFLATMENU, 0, &useFlat, 0) ? |
|
62 useFlat : false; |
|
63 } |
|
64 |
|
65 HANDLE |
|
66 nsUXThemeData::GetTheme(nsUXThemeClass cls) { |
|
67 NS_ASSERTION(cls < eUXNumClasses, "Invalid theme class!"); |
|
68 if(!sThemes[cls]) |
|
69 { |
|
70 sThemes[cls] = OpenThemeData(nullptr, GetClassName(cls)); |
|
71 } |
|
72 return sThemes[cls]; |
|
73 } |
|
74 |
|
75 HMODULE |
|
76 nsUXThemeData::GetThemeDLL() { |
|
77 if (!sThemeDLL) |
|
78 sThemeDLL = ::LoadLibraryW(kThemeLibraryName); |
|
79 return sThemeDLL; |
|
80 } |
|
81 |
|
82 const wchar_t *nsUXThemeData::GetClassName(nsUXThemeClass cls) { |
|
83 switch(cls) { |
|
84 case eUXButton: |
|
85 return L"Button"; |
|
86 case eUXEdit: |
|
87 return L"Edit"; |
|
88 case eUXTooltip: |
|
89 return L"Tooltip"; |
|
90 case eUXRebar: |
|
91 return L"Rebar"; |
|
92 case eUXMediaRebar: |
|
93 return L"Media::Rebar"; |
|
94 case eUXCommunicationsRebar: |
|
95 return L"Communications::Rebar"; |
|
96 case eUXBrowserTabBarRebar: |
|
97 return L"BrowserTabBar::Rebar"; |
|
98 case eUXToolbar: |
|
99 return L"Toolbar"; |
|
100 case eUXMediaToolbar: |
|
101 return L"Media::Toolbar"; |
|
102 case eUXCommunicationsToolbar: |
|
103 return L"Communications::Toolbar"; |
|
104 case eUXProgress: |
|
105 return L"Progress"; |
|
106 case eUXTab: |
|
107 return L"Tab"; |
|
108 case eUXScrollbar: |
|
109 return L"Scrollbar"; |
|
110 case eUXTrackbar: |
|
111 return L"Trackbar"; |
|
112 case eUXSpin: |
|
113 return L"Spin"; |
|
114 case eUXStatus: |
|
115 return L"Status"; |
|
116 case eUXCombobox: |
|
117 return L"Combobox"; |
|
118 case eUXHeader: |
|
119 return L"Header"; |
|
120 case eUXListview: |
|
121 return L"Listview"; |
|
122 case eUXMenu: |
|
123 return L"Menu"; |
|
124 case eUXWindowFrame: |
|
125 return L"Window"; |
|
126 default: |
|
127 NS_NOTREACHED("unknown uxtheme class"); |
|
128 return L""; |
|
129 } |
|
130 } |
|
131 |
|
132 // static |
|
133 void |
|
134 nsUXThemeData::InitTitlebarInfo() |
|
135 { |
|
136 // Pre-populate with generic metrics. These likley will not match |
|
137 // the current theme, but they insure the buttons at least show up. |
|
138 sCommandButtons[0].cx = GetSystemMetrics(SM_CXSIZE); |
|
139 sCommandButtons[0].cy = GetSystemMetrics(SM_CYSIZE); |
|
140 sCommandButtons[1].cx = sCommandButtons[2].cx = sCommandButtons[0].cx; |
|
141 sCommandButtons[1].cy = sCommandButtons[2].cy = sCommandButtons[0].cy; |
|
142 sCommandButtons[3].cx = sCommandButtons[0].cx * 3; |
|
143 sCommandButtons[3].cy = sCommandButtons[0].cy; |
|
144 |
|
145 // Use system metrics for pre-vista, otherwise trigger a |
|
146 // refresh on the next layout. |
|
147 sTitlebarInfoPopulatedAero = sTitlebarInfoPopulatedThemed = |
|
148 !IsVistaOrLater(); |
|
149 } |
|
150 |
|
151 // static |
|
152 void |
|
153 nsUXThemeData::UpdateTitlebarInfo(HWND aWnd) |
|
154 { |
|
155 if (!aWnd) |
|
156 return; |
|
157 |
|
158 if (!sTitlebarInfoPopulatedAero && nsUXThemeData::CheckForCompositor()) { |
|
159 RECT captionButtons; |
|
160 if (SUCCEEDED(WinUtils::dwmGetWindowAttributePtr(aWnd, |
|
161 DWMWA_CAPTION_BUTTON_BOUNDS, |
|
162 &captionButtons, |
|
163 sizeof(captionButtons)))) { |
|
164 sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cx = captionButtons.right - captionButtons.left - 3; |
|
165 sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cy = (captionButtons.bottom - captionButtons.top) - 1; |
|
166 sTitlebarInfoPopulatedAero = true; |
|
167 } |
|
168 } |
|
169 |
|
170 if (sTitlebarInfoPopulatedThemed) |
|
171 return; |
|
172 |
|
173 // Query a temporary, visible window with command buttons to get |
|
174 // the right metrics. |
|
175 nsAutoString className; |
|
176 className.AssignLiteral(kClassNameTemp); |
|
177 WNDCLASSW wc; |
|
178 wc.style = 0; |
|
179 wc.lpfnWndProc = ::DefWindowProcW; |
|
180 wc.cbClsExtra = 0; |
|
181 wc.cbWndExtra = 0; |
|
182 wc.hInstance = nsToolkit::mDllInstance; |
|
183 wc.hIcon = nullptr; |
|
184 wc.hCursor = nullptr; |
|
185 wc.hbrBackground = nullptr; |
|
186 wc.lpszMenuName = nullptr; |
|
187 wc.lpszClassName = className.get(); |
|
188 ::RegisterClassW(&wc); |
|
189 |
|
190 // Create a transparent descendant of the window passed in. This |
|
191 // keeps the window from showing up on the desktop or the taskbar. |
|
192 // Note the parent (browser) window is usually still hidden, we |
|
193 // don't want to display it, so we can't query it directly. |
|
194 HWND hWnd = CreateWindowExW(WS_EX_LAYERED, |
|
195 className.get(), L"", |
|
196 WS_OVERLAPPEDWINDOW, |
|
197 0, 0, 0, 0, aWnd, nullptr, |
|
198 nsToolkit::mDllInstance, nullptr); |
|
199 NS_ASSERTION(hWnd, "UpdateTitlebarInfo window creation failed."); |
|
200 |
|
201 ShowWindow(hWnd, SW_SHOW); |
|
202 TITLEBARINFOEX info = {0}; |
|
203 info.cbSize = sizeof(TITLEBARINFOEX); |
|
204 SendMessage(hWnd, WM_GETTITLEBARINFOEX, 0, (LPARAM)&info); |
|
205 DestroyWindow(hWnd); |
|
206 |
|
207 // Only set if we have valid data for all three buttons we use. |
|
208 if ((info.rgrect[2].right - info.rgrect[2].left) == 0 || |
|
209 (info.rgrect[3].right - info.rgrect[3].left) == 0 || |
|
210 (info.rgrect[5].right - info.rgrect[5].left) == 0) { |
|
211 NS_WARNING("WM_GETTITLEBARINFOEX query failed to find usable metrics."); |
|
212 return; |
|
213 } |
|
214 // minimize |
|
215 sCommandButtons[0].cx = info.rgrect[2].right - info.rgrect[2].left; |
|
216 sCommandButtons[0].cy = info.rgrect[2].bottom - info.rgrect[2].top; |
|
217 // maximize/restore |
|
218 sCommandButtons[1].cx = info.rgrect[3].right - info.rgrect[3].left; |
|
219 sCommandButtons[1].cy = info.rgrect[3].bottom - info.rgrect[3].top; |
|
220 // close |
|
221 sCommandButtons[2].cx = info.rgrect[5].right - info.rgrect[5].left; |
|
222 sCommandButtons[2].cy = info.rgrect[5].bottom - info.rgrect[5].top; |
|
223 |
|
224 sTitlebarInfoPopulatedThemed = true; |
|
225 } |
|
226 |
|
227 // visual style (aero glass, aero basic) |
|
228 // theme (aero, luna, zune) |
|
229 // theme color (silver, olive, blue) |
|
230 // system colors |
|
231 |
|
232 struct THEMELIST { |
|
233 LPCWSTR name; |
|
234 int type; |
|
235 }; |
|
236 |
|
237 const THEMELIST knownThemes[] = { |
|
238 { L"aero.msstyles", WINTHEME_AERO }, |
|
239 { L"aerolite.msstyles", WINTHEME_AERO_LITE }, |
|
240 { L"luna.msstyles", WINTHEME_LUNA }, |
|
241 { L"zune.msstyles", WINTHEME_ZUNE }, |
|
242 { L"royale.msstyles", WINTHEME_ROYALE } |
|
243 }; |
|
244 |
|
245 const THEMELIST knownColors[] = { |
|
246 { L"normalcolor", WINTHEMECOLOR_NORMAL }, |
|
247 { L"homestead", WINTHEMECOLOR_HOMESTEAD }, |
|
248 { L"metallic", WINTHEMECOLOR_METALLIC } |
|
249 }; |
|
250 |
|
251 LookAndFeel::WindowsTheme |
|
252 nsUXThemeData::sThemeId = LookAndFeel::eWindowsTheme_Generic; |
|
253 |
|
254 bool |
|
255 nsUXThemeData::sIsDefaultWindowsTheme = false; |
|
256 bool |
|
257 nsUXThemeData::sIsHighContrastOn = false; |
|
258 |
|
259 // static |
|
260 LookAndFeel::WindowsTheme |
|
261 nsUXThemeData::GetNativeThemeId() |
|
262 { |
|
263 return sThemeId; |
|
264 } |
|
265 |
|
266 // static |
|
267 bool nsUXThemeData::IsDefaultWindowTheme() |
|
268 { |
|
269 return sIsDefaultWindowsTheme; |
|
270 } |
|
271 |
|
272 bool nsUXThemeData::IsHighContrastOn() |
|
273 { |
|
274 return sIsHighContrastOn; |
|
275 } |
|
276 |
|
277 // static |
|
278 bool nsUXThemeData::CheckForCompositor(bool aUpdateCache) |
|
279 { |
|
280 static BOOL sCachedValue = FALSE; |
|
281 if (aUpdateCache && WinUtils::dwmIsCompositionEnabledPtr) { |
|
282 WinUtils::dwmIsCompositionEnabledPtr(&sCachedValue); |
|
283 } |
|
284 return sCachedValue; |
|
285 } |
|
286 |
|
287 // static |
|
288 void |
|
289 nsUXThemeData::UpdateNativeThemeInfo() |
|
290 { |
|
291 // Trigger a refresh of themed button metrics if needed |
|
292 sTitlebarInfoPopulatedThemed = !IsVistaOrLater(); |
|
293 |
|
294 sIsDefaultWindowsTheme = false; |
|
295 sThemeId = LookAndFeel::eWindowsTheme_Generic; |
|
296 |
|
297 HIGHCONTRAST highContrastInfo; |
|
298 highContrastInfo.cbSize = sizeof(HIGHCONTRAST); |
|
299 if (SystemParametersInfo(SPI_GETHIGHCONTRAST, 0, &highContrastInfo, 0)) { |
|
300 sIsHighContrastOn = ((highContrastInfo.dwFlags & HCF_HIGHCONTRASTON) != 0); |
|
301 } else { |
|
302 sIsHighContrastOn = false; |
|
303 } |
|
304 |
|
305 if (!IsAppThemed()) { |
|
306 sThemeId = LookAndFeel::eWindowsTheme_Classic; |
|
307 return; |
|
308 } |
|
309 |
|
310 WCHAR themeFileName[MAX_PATH + 1]; |
|
311 WCHAR themeColor[MAX_PATH + 1]; |
|
312 if (FAILED(GetCurrentThemeName(themeFileName, |
|
313 MAX_PATH, |
|
314 themeColor, |
|
315 MAX_PATH, |
|
316 nullptr, 0))) { |
|
317 sThemeId = LookAndFeel::eWindowsTheme_Classic; |
|
318 return; |
|
319 } |
|
320 |
|
321 LPCWSTR themeName = wcsrchr(themeFileName, L'\\'); |
|
322 themeName = themeName ? themeName + 1 : themeFileName; |
|
323 |
|
324 WindowsTheme theme = WINTHEME_UNRECOGNIZED; |
|
325 for (size_t i = 0; i < ArrayLength(knownThemes); ++i) { |
|
326 if (!lstrcmpiW(themeName, knownThemes[i].name)) { |
|
327 theme = (WindowsTheme)knownThemes[i].type; |
|
328 break; |
|
329 } |
|
330 } |
|
331 |
|
332 if (theme == WINTHEME_UNRECOGNIZED) |
|
333 return; |
|
334 |
|
335 // We're using the default theme if we're using any of Aero, Aero Lite, or |
|
336 // luna. However, on Win8, GetCurrentThemeName (see above) returns |
|
337 // AeroLite.msstyles for the 4 builtin highcontrast themes as well. Those |
|
338 // themes "don't count" as default themes, so we specifically check for high |
|
339 // contrast mode in that situation. |
|
340 if (!(IsWin8OrLater() && sIsHighContrastOn) && |
|
341 (theme == WINTHEME_AERO || theme == WINTHEME_AERO_LITE || theme == WINTHEME_LUNA)) { |
|
342 sIsDefaultWindowsTheme = true; |
|
343 } |
|
344 |
|
345 if (theme != WINTHEME_LUNA) { |
|
346 switch(theme) { |
|
347 case WINTHEME_AERO: |
|
348 sThemeId = LookAndFeel::eWindowsTheme_Aero; |
|
349 return; |
|
350 case WINTHEME_AERO_LITE: |
|
351 sThemeId = LookAndFeel::eWindowsTheme_AeroLite; |
|
352 return; |
|
353 case WINTHEME_ZUNE: |
|
354 sThemeId = LookAndFeel::eWindowsTheme_Zune; |
|
355 return; |
|
356 case WINTHEME_ROYALE: |
|
357 sThemeId = LookAndFeel::eWindowsTheme_Royale; |
|
358 return; |
|
359 default: |
|
360 NS_WARNING("unhandled theme type."); |
|
361 return; |
|
362 } |
|
363 } |
|
364 |
|
365 // calculate the luna color scheme |
|
366 WindowsThemeColor color = WINTHEMECOLOR_UNRECOGNIZED; |
|
367 for (size_t i = 0; i < ArrayLength(knownColors); ++i) { |
|
368 if (!lstrcmpiW(themeColor, knownColors[i].name)) { |
|
369 color = (WindowsThemeColor)knownColors[i].type; |
|
370 break; |
|
371 } |
|
372 } |
|
373 |
|
374 switch(color) { |
|
375 case WINTHEMECOLOR_NORMAL: |
|
376 sThemeId = LookAndFeel::eWindowsTheme_LunaBlue; |
|
377 return; |
|
378 case WINTHEMECOLOR_HOMESTEAD: |
|
379 sThemeId = LookAndFeel::eWindowsTheme_LunaOlive; |
|
380 return; |
|
381 case WINTHEMECOLOR_METALLIC: |
|
382 sThemeId = LookAndFeel::eWindowsTheme_LunaSilver; |
|
383 return; |
|
384 default: |
|
385 NS_WARNING("unhandled theme color."); |
|
386 return; |
|
387 } |
|
388 } |