widget/windows/nsUXThemeData.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial