1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/windows/nsUXThemeData.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,388 @@ 1.4 +/* vim: se cin sw=2 ts=2 et : */ 1.5 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.6 + * 1.7 + * This Source Code Form is subject to the terms of the Mozilla Public 1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.10 + 1.11 +#include "mozilla/ArrayUtils.h" 1.12 +#include "mozilla/WindowsVersion.h" 1.13 + 1.14 +#include "nsUXThemeData.h" 1.15 +#include "nsDebug.h" 1.16 +#include "nsToolkit.h" 1.17 +#include "nsUXThemeConstants.h" 1.18 + 1.19 +using namespace mozilla; 1.20 +using namespace mozilla::widget; 1.21 + 1.22 +const wchar_t 1.23 +nsUXThemeData::kThemeLibraryName[] = L"uxtheme.dll"; 1.24 + 1.25 +HANDLE 1.26 +nsUXThemeData::sThemes[eUXNumClasses]; 1.27 + 1.28 +HMODULE 1.29 +nsUXThemeData::sThemeDLL = nullptr; 1.30 + 1.31 +bool 1.32 +nsUXThemeData::sFlatMenus = false; 1.33 + 1.34 +bool nsUXThemeData::sTitlebarInfoPopulatedAero = false; 1.35 +bool nsUXThemeData::sTitlebarInfoPopulatedThemed = false; 1.36 +SIZE nsUXThemeData::sCommandButtons[4]; 1.37 + 1.38 +void 1.39 +nsUXThemeData::Teardown() { 1.40 + Invalidate(); 1.41 + if(sThemeDLL) 1.42 + FreeLibrary(sThemeDLL); 1.43 +} 1.44 + 1.45 +void 1.46 +nsUXThemeData::Initialize() 1.47 +{ 1.48 + ::ZeroMemory(sThemes, sizeof(sThemes)); 1.49 + NS_ASSERTION(!sThemeDLL, "nsUXThemeData being initialized twice!"); 1.50 + 1.51 + CheckForCompositor(true); 1.52 + Invalidate(); 1.53 +} 1.54 + 1.55 +void 1.56 +nsUXThemeData::Invalidate() { 1.57 + for(int i = 0; i < eUXNumClasses; i++) { 1.58 + if(sThemes[i]) { 1.59 + CloseThemeData(sThemes[i]); 1.60 + sThemes[i] = nullptr; 1.61 + } 1.62 + } 1.63 + BOOL useFlat = FALSE; 1.64 + sFlatMenus = ::SystemParametersInfo(SPI_GETFLATMENU, 0, &useFlat, 0) ? 1.65 + useFlat : false; 1.66 +} 1.67 + 1.68 +HANDLE 1.69 +nsUXThemeData::GetTheme(nsUXThemeClass cls) { 1.70 + NS_ASSERTION(cls < eUXNumClasses, "Invalid theme class!"); 1.71 + if(!sThemes[cls]) 1.72 + { 1.73 + sThemes[cls] = OpenThemeData(nullptr, GetClassName(cls)); 1.74 + } 1.75 + return sThemes[cls]; 1.76 +} 1.77 + 1.78 +HMODULE 1.79 +nsUXThemeData::GetThemeDLL() { 1.80 + if (!sThemeDLL) 1.81 + sThemeDLL = ::LoadLibraryW(kThemeLibraryName); 1.82 + return sThemeDLL; 1.83 +} 1.84 + 1.85 +const wchar_t *nsUXThemeData::GetClassName(nsUXThemeClass cls) { 1.86 + switch(cls) { 1.87 + case eUXButton: 1.88 + return L"Button"; 1.89 + case eUXEdit: 1.90 + return L"Edit"; 1.91 + case eUXTooltip: 1.92 + return L"Tooltip"; 1.93 + case eUXRebar: 1.94 + return L"Rebar"; 1.95 + case eUXMediaRebar: 1.96 + return L"Media::Rebar"; 1.97 + case eUXCommunicationsRebar: 1.98 + return L"Communications::Rebar"; 1.99 + case eUXBrowserTabBarRebar: 1.100 + return L"BrowserTabBar::Rebar"; 1.101 + case eUXToolbar: 1.102 + return L"Toolbar"; 1.103 + case eUXMediaToolbar: 1.104 + return L"Media::Toolbar"; 1.105 + case eUXCommunicationsToolbar: 1.106 + return L"Communications::Toolbar"; 1.107 + case eUXProgress: 1.108 + return L"Progress"; 1.109 + case eUXTab: 1.110 + return L"Tab"; 1.111 + case eUXScrollbar: 1.112 + return L"Scrollbar"; 1.113 + case eUXTrackbar: 1.114 + return L"Trackbar"; 1.115 + case eUXSpin: 1.116 + return L"Spin"; 1.117 + case eUXStatus: 1.118 + return L"Status"; 1.119 + case eUXCombobox: 1.120 + return L"Combobox"; 1.121 + case eUXHeader: 1.122 + return L"Header"; 1.123 + case eUXListview: 1.124 + return L"Listview"; 1.125 + case eUXMenu: 1.126 + return L"Menu"; 1.127 + case eUXWindowFrame: 1.128 + return L"Window"; 1.129 + default: 1.130 + NS_NOTREACHED("unknown uxtheme class"); 1.131 + return L""; 1.132 + } 1.133 +} 1.134 + 1.135 +// static 1.136 +void 1.137 +nsUXThemeData::InitTitlebarInfo() 1.138 +{ 1.139 + // Pre-populate with generic metrics. These likley will not match 1.140 + // the current theme, but they insure the buttons at least show up. 1.141 + sCommandButtons[0].cx = GetSystemMetrics(SM_CXSIZE); 1.142 + sCommandButtons[0].cy = GetSystemMetrics(SM_CYSIZE); 1.143 + sCommandButtons[1].cx = sCommandButtons[2].cx = sCommandButtons[0].cx; 1.144 + sCommandButtons[1].cy = sCommandButtons[2].cy = sCommandButtons[0].cy; 1.145 + sCommandButtons[3].cx = sCommandButtons[0].cx * 3; 1.146 + sCommandButtons[3].cy = sCommandButtons[0].cy; 1.147 + 1.148 + // Use system metrics for pre-vista, otherwise trigger a 1.149 + // refresh on the next layout. 1.150 + sTitlebarInfoPopulatedAero = sTitlebarInfoPopulatedThemed = 1.151 + !IsVistaOrLater(); 1.152 +} 1.153 + 1.154 +// static 1.155 +void 1.156 +nsUXThemeData::UpdateTitlebarInfo(HWND aWnd) 1.157 +{ 1.158 + if (!aWnd) 1.159 + return; 1.160 + 1.161 + if (!sTitlebarInfoPopulatedAero && nsUXThemeData::CheckForCompositor()) { 1.162 + RECT captionButtons; 1.163 + if (SUCCEEDED(WinUtils::dwmGetWindowAttributePtr(aWnd, 1.164 + DWMWA_CAPTION_BUTTON_BOUNDS, 1.165 + &captionButtons, 1.166 + sizeof(captionButtons)))) { 1.167 + sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cx = captionButtons.right - captionButtons.left - 3; 1.168 + sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cy = (captionButtons.bottom - captionButtons.top) - 1; 1.169 + sTitlebarInfoPopulatedAero = true; 1.170 + } 1.171 + } 1.172 + 1.173 + if (sTitlebarInfoPopulatedThemed) 1.174 + return; 1.175 + 1.176 + // Query a temporary, visible window with command buttons to get 1.177 + // the right metrics. 1.178 + nsAutoString className; 1.179 + className.AssignLiteral(kClassNameTemp); 1.180 + WNDCLASSW wc; 1.181 + wc.style = 0; 1.182 + wc.lpfnWndProc = ::DefWindowProcW; 1.183 + wc.cbClsExtra = 0; 1.184 + wc.cbWndExtra = 0; 1.185 + wc.hInstance = nsToolkit::mDllInstance; 1.186 + wc.hIcon = nullptr; 1.187 + wc.hCursor = nullptr; 1.188 + wc.hbrBackground = nullptr; 1.189 + wc.lpszMenuName = nullptr; 1.190 + wc.lpszClassName = className.get(); 1.191 + ::RegisterClassW(&wc); 1.192 + 1.193 + // Create a transparent descendant of the window passed in. This 1.194 + // keeps the window from showing up on the desktop or the taskbar. 1.195 + // Note the parent (browser) window is usually still hidden, we 1.196 + // don't want to display it, so we can't query it directly. 1.197 + HWND hWnd = CreateWindowExW(WS_EX_LAYERED, 1.198 + className.get(), L"", 1.199 + WS_OVERLAPPEDWINDOW, 1.200 + 0, 0, 0, 0, aWnd, nullptr, 1.201 + nsToolkit::mDllInstance, nullptr); 1.202 + NS_ASSERTION(hWnd, "UpdateTitlebarInfo window creation failed."); 1.203 + 1.204 + ShowWindow(hWnd, SW_SHOW); 1.205 + TITLEBARINFOEX info = {0}; 1.206 + info.cbSize = sizeof(TITLEBARINFOEX); 1.207 + SendMessage(hWnd, WM_GETTITLEBARINFOEX, 0, (LPARAM)&info); 1.208 + DestroyWindow(hWnd); 1.209 + 1.210 + // Only set if we have valid data for all three buttons we use. 1.211 + if ((info.rgrect[2].right - info.rgrect[2].left) == 0 || 1.212 + (info.rgrect[3].right - info.rgrect[3].left) == 0 || 1.213 + (info.rgrect[5].right - info.rgrect[5].left) == 0) { 1.214 + NS_WARNING("WM_GETTITLEBARINFOEX query failed to find usable metrics."); 1.215 + return; 1.216 + } 1.217 + // minimize 1.218 + sCommandButtons[0].cx = info.rgrect[2].right - info.rgrect[2].left; 1.219 + sCommandButtons[0].cy = info.rgrect[2].bottom - info.rgrect[2].top; 1.220 + // maximize/restore 1.221 + sCommandButtons[1].cx = info.rgrect[3].right - info.rgrect[3].left; 1.222 + sCommandButtons[1].cy = info.rgrect[3].bottom - info.rgrect[3].top; 1.223 + // close 1.224 + sCommandButtons[2].cx = info.rgrect[5].right - info.rgrect[5].left; 1.225 + sCommandButtons[2].cy = info.rgrect[5].bottom - info.rgrect[5].top; 1.226 + 1.227 + sTitlebarInfoPopulatedThemed = true; 1.228 +} 1.229 + 1.230 +// visual style (aero glass, aero basic) 1.231 +// theme (aero, luna, zune) 1.232 +// theme color (silver, olive, blue) 1.233 +// system colors 1.234 + 1.235 +struct THEMELIST { 1.236 + LPCWSTR name; 1.237 + int type; 1.238 +}; 1.239 + 1.240 +const THEMELIST knownThemes[] = { 1.241 + { L"aero.msstyles", WINTHEME_AERO }, 1.242 + { L"aerolite.msstyles", WINTHEME_AERO_LITE }, 1.243 + { L"luna.msstyles", WINTHEME_LUNA }, 1.244 + { L"zune.msstyles", WINTHEME_ZUNE }, 1.245 + { L"royale.msstyles", WINTHEME_ROYALE } 1.246 +}; 1.247 + 1.248 +const THEMELIST knownColors[] = { 1.249 + { L"normalcolor", WINTHEMECOLOR_NORMAL }, 1.250 + { L"homestead", WINTHEMECOLOR_HOMESTEAD }, 1.251 + { L"metallic", WINTHEMECOLOR_METALLIC } 1.252 +}; 1.253 + 1.254 +LookAndFeel::WindowsTheme 1.255 +nsUXThemeData::sThemeId = LookAndFeel::eWindowsTheme_Generic; 1.256 + 1.257 +bool 1.258 +nsUXThemeData::sIsDefaultWindowsTheme = false; 1.259 +bool 1.260 +nsUXThemeData::sIsHighContrastOn = false; 1.261 + 1.262 +// static 1.263 +LookAndFeel::WindowsTheme 1.264 +nsUXThemeData::GetNativeThemeId() 1.265 +{ 1.266 + return sThemeId; 1.267 +} 1.268 + 1.269 +// static 1.270 +bool nsUXThemeData::IsDefaultWindowTheme() 1.271 +{ 1.272 + return sIsDefaultWindowsTheme; 1.273 +} 1.274 + 1.275 +bool nsUXThemeData::IsHighContrastOn() 1.276 +{ 1.277 + return sIsHighContrastOn; 1.278 +} 1.279 + 1.280 +// static 1.281 +bool nsUXThemeData::CheckForCompositor(bool aUpdateCache) 1.282 +{ 1.283 + static BOOL sCachedValue = FALSE; 1.284 + if (aUpdateCache && WinUtils::dwmIsCompositionEnabledPtr) { 1.285 + WinUtils::dwmIsCompositionEnabledPtr(&sCachedValue); 1.286 + } 1.287 + return sCachedValue; 1.288 +} 1.289 + 1.290 +// static 1.291 +void 1.292 +nsUXThemeData::UpdateNativeThemeInfo() 1.293 +{ 1.294 + // Trigger a refresh of themed button metrics if needed 1.295 + sTitlebarInfoPopulatedThemed = !IsVistaOrLater(); 1.296 + 1.297 + sIsDefaultWindowsTheme = false; 1.298 + sThemeId = LookAndFeel::eWindowsTheme_Generic; 1.299 + 1.300 + HIGHCONTRAST highContrastInfo; 1.301 + highContrastInfo.cbSize = sizeof(HIGHCONTRAST); 1.302 + if (SystemParametersInfo(SPI_GETHIGHCONTRAST, 0, &highContrastInfo, 0)) { 1.303 + sIsHighContrastOn = ((highContrastInfo.dwFlags & HCF_HIGHCONTRASTON) != 0); 1.304 + } else { 1.305 + sIsHighContrastOn = false; 1.306 + } 1.307 + 1.308 + if (!IsAppThemed()) { 1.309 + sThemeId = LookAndFeel::eWindowsTheme_Classic; 1.310 + return; 1.311 + } 1.312 + 1.313 + WCHAR themeFileName[MAX_PATH + 1]; 1.314 + WCHAR themeColor[MAX_PATH + 1]; 1.315 + if (FAILED(GetCurrentThemeName(themeFileName, 1.316 + MAX_PATH, 1.317 + themeColor, 1.318 + MAX_PATH, 1.319 + nullptr, 0))) { 1.320 + sThemeId = LookAndFeel::eWindowsTheme_Classic; 1.321 + return; 1.322 + } 1.323 + 1.324 + LPCWSTR themeName = wcsrchr(themeFileName, L'\\'); 1.325 + themeName = themeName ? themeName + 1 : themeFileName; 1.326 + 1.327 + WindowsTheme theme = WINTHEME_UNRECOGNIZED; 1.328 + for (size_t i = 0; i < ArrayLength(knownThemes); ++i) { 1.329 + if (!lstrcmpiW(themeName, knownThemes[i].name)) { 1.330 + theme = (WindowsTheme)knownThemes[i].type; 1.331 + break; 1.332 + } 1.333 + } 1.334 + 1.335 + if (theme == WINTHEME_UNRECOGNIZED) 1.336 + return; 1.337 + 1.338 + // We're using the default theme if we're using any of Aero, Aero Lite, or 1.339 + // luna. However, on Win8, GetCurrentThemeName (see above) returns 1.340 + // AeroLite.msstyles for the 4 builtin highcontrast themes as well. Those 1.341 + // themes "don't count" as default themes, so we specifically check for high 1.342 + // contrast mode in that situation. 1.343 + if (!(IsWin8OrLater() && sIsHighContrastOn) && 1.344 + (theme == WINTHEME_AERO || theme == WINTHEME_AERO_LITE || theme == WINTHEME_LUNA)) { 1.345 + sIsDefaultWindowsTheme = true; 1.346 + } 1.347 + 1.348 + if (theme != WINTHEME_LUNA) { 1.349 + switch(theme) { 1.350 + case WINTHEME_AERO: 1.351 + sThemeId = LookAndFeel::eWindowsTheme_Aero; 1.352 + return; 1.353 + case WINTHEME_AERO_LITE: 1.354 + sThemeId = LookAndFeel::eWindowsTheme_AeroLite; 1.355 + return; 1.356 + case WINTHEME_ZUNE: 1.357 + sThemeId = LookAndFeel::eWindowsTheme_Zune; 1.358 + return; 1.359 + case WINTHEME_ROYALE: 1.360 + sThemeId = LookAndFeel::eWindowsTheme_Royale; 1.361 + return; 1.362 + default: 1.363 + NS_WARNING("unhandled theme type."); 1.364 + return; 1.365 + } 1.366 + } 1.367 + 1.368 + // calculate the luna color scheme 1.369 + WindowsThemeColor color = WINTHEMECOLOR_UNRECOGNIZED; 1.370 + for (size_t i = 0; i < ArrayLength(knownColors); ++i) { 1.371 + if (!lstrcmpiW(themeColor, knownColors[i].name)) { 1.372 + color = (WindowsThemeColor)knownColors[i].type; 1.373 + break; 1.374 + } 1.375 + } 1.376 + 1.377 + switch(color) { 1.378 + case WINTHEMECOLOR_NORMAL: 1.379 + sThemeId = LookAndFeel::eWindowsTheme_LunaBlue; 1.380 + return; 1.381 + case WINTHEMECOLOR_HOMESTEAD: 1.382 + sThemeId = LookAndFeel::eWindowsTheme_LunaOlive; 1.383 + return; 1.384 + case WINTHEMECOLOR_METALLIC: 1.385 + sThemeId = LookAndFeel::eWindowsTheme_LunaSilver; 1.386 + return; 1.387 + default: 1.388 + NS_WARNING("unhandled theme color."); 1.389 + return; 1.390 + } 1.391 +}