Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / widget / windows / nsLookAndFeel.cpp
blob844872cd3d6fee98f6ccfabf51c0def05adb0bf1
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsLookAndFeel.h"
7 #include <stdint.h>
8 #include <windows.h>
9 #include <shellapi.h>
10 #include "nsStyleConsts.h"
11 #include "nsUXThemeData.h"
12 #include "nsUXThemeConstants.h"
13 #include "nsWindowsHelpers.h"
14 #include "WinUtils.h"
15 #include "WindowsUIUtils.h"
16 #include "mozilla/FontPropertyTypes.h"
17 #include "mozilla/Telemetry.h"
18 #include "gfxFontConstants.h"
19 #include "gfxWindowsPlatform.h"
20 #include "mozilla/StaticPrefs_widget.h"
22 using namespace mozilla;
23 using namespace mozilla::widget;
25 static Maybe<nscolor> GetColorFromTheme(nsUXThemeClass cls, int32_t aPart,
26 int32_t aState, int32_t aPropId) {
27 COLORREF color;
28 HRESULT hr = GetThemeColor(nsUXThemeData::GetTheme(cls), aPart, aState,
29 aPropId, &color);
30 if (hr == S_OK) {
31 return Some(COLOREF_2_NSRGB(color));
33 return Nothing();
36 static int32_t GetSystemParam(long flag, int32_t def) {
37 DWORD value;
38 return ::SystemParametersInfo(flag, 0, &value, 0) ? value : def;
41 static nsresult SystemWantsDarkTheme(int32_t& darkThemeEnabled) {
42 nsresult rv = NS_OK;
43 nsCOMPtr<nsIWindowsRegKey> personalizeKey =
44 do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
45 if (NS_WARN_IF(NS_FAILED(rv))) {
46 return rv;
49 rv = personalizeKey->Open(
50 nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
51 nsLiteralString(
52 u"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"),
53 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
54 if (NS_FAILED(rv)) {
55 return rv;
58 uint32_t lightThemeEnabled;
59 rv =
60 personalizeKey->ReadIntValue(u"AppsUseLightTheme"_ns, &lightThemeEnabled);
61 if (NS_SUCCEEDED(rv)) {
62 darkThemeEnabled = !lightThemeEnabled;
65 return rv;
68 static int32_t SystemColorFilter() {
69 nsresult rv = NS_OK;
70 nsCOMPtr<nsIWindowsRegKey> colorFilteringKey =
71 do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
72 if (NS_WARN_IF(NS_FAILED(rv))) {
73 return 0;
76 rv = colorFilteringKey->Open(
77 nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
78 u"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Accessibility\\ATConfig\\colorfiltering"_ns,
79 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
80 if (NS_FAILED(rv)) {
81 return 0;
84 // The Active value is set to 1 when the "Turn on color filters" setting
85 // in the Color filters section of Windows' Ease of Access settings is turned
86 // on. If it is disabled (Active == 0 or does not exist), do not report having
87 // a color filter.
88 uint32_t active;
89 rv = colorFilteringKey->ReadIntValue(u"Active"_ns, &active);
90 if (NS_FAILED(rv) || active == 0) {
91 return 0;
94 // The FilterType value is set to whichever filter is enabled.
95 uint32_t filterType;
96 rv = colorFilteringKey->ReadIntValue(u"FilterType"_ns, &filterType);
97 if (NS_SUCCEEDED(rv)) {
98 return filterType;
101 return 0;
104 nsLookAndFeel::nsLookAndFeel() {
105 mozilla::Telemetry::Accumulate(mozilla::Telemetry::TOUCH_ENABLED_DEVICE,
106 WinUtils::IsTouchDeviceSupportPresent());
109 nsLookAndFeel::~nsLookAndFeel() = default;
111 void nsLookAndFeel::NativeInit() { EnsureInit(); }
113 /* virtual */
114 void nsLookAndFeel::RefreshImpl() {
115 mInitialized = false; // Fetch system colors next time they're used.
116 nsXPLookAndFeel::RefreshImpl();
119 static bool UseNonNativeMenuColors(ColorScheme aScheme) {
120 return !LookAndFeel::GetInt(LookAndFeel::IntID::UseAccessibilityTheme) ||
121 aScheme == ColorScheme::Dark;
124 nsresult nsLookAndFeel::NativeGetColor(ColorID aID, ColorScheme aScheme,
125 nscolor& aColor) {
126 EnsureInit();
128 auto IsHighlightColor = [&] {
129 switch (aID) {
130 case ColorID::MozMenuhover:
131 return !UseNonNativeMenuColors(aScheme);
132 case ColorID::Highlight:
133 case ColorID::Selecteditem:
134 // We prefer the generic dark selection color if we don't have an
135 // explicit one.
136 return aScheme != ColorScheme::Dark || mDarkHighlight;
137 case ColorID::IMESelectedRawTextBackground:
138 case ColorID::IMESelectedConvertedTextBackground:
139 return true;
140 default:
141 return false;
145 auto IsHighlightTextColor = [&] {
146 switch (aID) {
147 case ColorID::MozMenubarhovertext:
148 if (UseNonNativeMenuColors(aScheme)) {
149 return false;
151 [[fallthrough]];
152 case ColorID::MozMenuhovertext:
153 if (UseNonNativeMenuColors(aScheme)) {
154 return false;
156 return !mColorMenuHoverText;
157 case ColorID::Highlighttext:
158 case ColorID::Selecteditemtext:
159 // We prefer the generic dark selection color if we don't have an
160 // explicit one.
161 return aScheme != ColorScheme::Dark || mDarkHighlightText;
162 case ColorID::IMESelectedRawTextForeground:
163 case ColorID::IMESelectedConvertedTextForeground:
164 return true;
165 default:
166 return false;
170 if (IsHighlightColor()) {
171 if (aScheme == ColorScheme::Dark && mDarkHighlight) {
172 aColor = *mDarkHighlight;
173 } else {
174 aColor = GetColorForSysColorIndex(COLOR_HIGHLIGHT);
176 return NS_OK;
179 if (IsHighlightTextColor()) {
180 if (aScheme == ColorScheme::Dark && mDarkHighlightText) {
181 aColor = *mDarkHighlightText;
182 } else {
183 aColor = GetColorForSysColorIndex(COLOR_HIGHLIGHTTEXT);
185 return NS_OK;
188 // Titlebar colors are color-scheme aware.
189 switch (aID) {
190 case ColorID::Activecaption:
191 aColor = mTitlebarColors.Get(aScheme, true).mBg;
192 return NS_OK;
193 case ColorID::Captiontext:
194 aColor = mTitlebarColors.Get(aScheme, true).mFg;
195 return NS_OK;
196 case ColorID::Activeborder:
197 aColor = mTitlebarColors.Get(aScheme, true).mBorder;
198 return NS_OK;
199 case ColorID::Inactivecaption:
200 aColor = mTitlebarColors.Get(aScheme, false).mBg;
201 return NS_OK;
202 case ColorID::Inactivecaptiontext:
203 aColor = mTitlebarColors.Get(aScheme, false).mFg;
204 return NS_OK;
205 case ColorID::Inactiveborder:
206 aColor = mTitlebarColors.Get(aScheme, false).mBorder;
207 return NS_OK;
208 default:
209 break;
212 if (aScheme == ColorScheme::Dark) {
213 if (auto color = GenericDarkColor(aID)) {
214 aColor = *color;
215 return NS_OK;
219 static constexpr auto kNonNativeMenuText = NS_RGB(0x15, 0x14, 0x1a);
220 nsresult res = NS_OK;
221 int idx;
222 switch (aID) {
223 case ColorID::IMERawInputBackground:
224 case ColorID::IMEConvertedTextBackground:
225 aColor = NS_TRANSPARENT;
226 return NS_OK;
227 case ColorID::IMERawInputForeground:
228 case ColorID::IMEConvertedTextForeground:
229 aColor = NS_SAME_AS_FOREGROUND_COLOR;
230 return NS_OK;
231 case ColorID::IMERawInputUnderline:
232 case ColorID::IMEConvertedTextUnderline:
233 aColor = NS_SAME_AS_FOREGROUND_COLOR;
234 return NS_OK;
235 case ColorID::IMESelectedRawTextUnderline:
236 case ColorID::IMESelectedConvertedTextUnderline:
237 aColor = NS_TRANSPARENT;
238 return NS_OK;
240 // New CSS 2 Color definitions
241 case ColorID::Appworkspace:
242 idx = COLOR_APPWORKSPACE;
243 break;
244 case ColorID::Background:
245 idx = COLOR_BACKGROUND;
246 break;
247 case ColorID::Buttonface:
248 case ColorID::MozButtonhoverface:
249 case ColorID::MozButtonactiveface:
250 case ColorID::MozButtondisabledface:
251 idx = COLOR_BTNFACE;
252 break;
253 case ColorID::Buttonhighlight:
254 idx = COLOR_BTNHIGHLIGHT;
255 break;
256 case ColorID::Buttonshadow:
257 idx = COLOR_BTNSHADOW;
258 break;
259 case ColorID::Buttontext:
260 case ColorID::MozButtonhovertext:
261 case ColorID::MozButtonactivetext:
262 idx = COLOR_BTNTEXT;
263 break;
264 case ColorID::MozCellhighlighttext:
265 aColor = NS_RGB(0, 0, 0);
266 return NS_OK;
267 case ColorID::MozCellhighlight:
268 aColor = NS_RGB(206, 206, 206);
269 return NS_OK;
270 case ColorID::Graytext:
271 idx = COLOR_GRAYTEXT;
272 break;
273 case ColorID::MozMenubarhovertext:
274 if (UseNonNativeMenuColors(aScheme)) {
275 aColor = kNonNativeMenuText;
276 return NS_OK;
278 [[fallthrough]];
279 case ColorID::MozMenuhovertext:
280 if (UseNonNativeMenuColors(aScheme)) {
281 aColor = kNonNativeMenuText;
282 return NS_OK;
284 if (mColorMenuHoverText) {
285 aColor = *mColorMenuHoverText;
286 return NS_OK;
288 idx = COLOR_HIGHLIGHTTEXT;
289 break;
290 case ColorID::MozMenuhover:
291 MOZ_ASSERT(UseNonNativeMenuColors(aScheme));
292 aColor = NS_RGB(0xe0, 0xe0, 0xe6);
293 return NS_OK;
294 case ColorID::MozMenuhoverdisabled:
295 if (UseNonNativeMenuColors(aScheme)) {
296 aColor = NS_RGB(0xf0, 0xf0, 0xf3);
297 return NS_OK;
299 aColor = NS_TRANSPARENT;
300 return NS_OK;
301 case ColorID::Infobackground:
302 idx = COLOR_INFOBK;
303 break;
304 case ColorID::Infotext:
305 idx = COLOR_INFOTEXT;
306 break;
307 case ColorID::Menu:
308 if (UseNonNativeMenuColors(aScheme)) {
309 aColor = NS_RGB(0xf9, 0xf9, 0xfb);
310 return NS_OK;
312 idx = COLOR_MENU;
313 break;
314 case ColorID::Menutext:
315 if (UseNonNativeMenuColors(aScheme)) {
316 aColor = kNonNativeMenuText;
317 return NS_OK;
319 idx = COLOR_MENUTEXT;
320 break;
321 case ColorID::Scrollbar:
322 idx = COLOR_SCROLLBAR;
323 break;
324 case ColorID::Threeddarkshadow:
325 idx = COLOR_3DDKSHADOW;
326 break;
327 case ColorID::Threedface:
328 idx = COLOR_3DFACE;
329 break;
330 case ColorID::Threedhighlight:
331 idx = COLOR_3DHIGHLIGHT;
332 break;
333 case ColorID::Threedlightshadow:
334 case ColorID::Buttonborder:
335 case ColorID::MozDisabledfield:
336 idx = COLOR_3DLIGHT;
337 break;
338 case ColorID::Threedshadow:
339 idx = COLOR_3DSHADOW;
340 break;
341 case ColorID::Window:
342 idx = COLOR_WINDOW;
343 break;
344 case ColorID::Windowframe:
345 idx = COLOR_WINDOWFRAME;
346 break;
347 case ColorID::Windowtext:
348 idx = COLOR_WINDOWTEXT;
349 break;
350 case ColorID::MozEventreerow:
351 case ColorID::MozOddtreerow:
352 case ColorID::Field:
353 case ColorID::MozCombobox:
354 idx = COLOR_WINDOW;
355 break;
356 case ColorID::Fieldtext:
357 case ColorID::MozComboboxtext:
358 idx = COLOR_WINDOWTEXT;
359 break;
360 case ColorID::MozHeaderbar:
361 case ColorID::MozHeaderbarinactive:
362 case ColorID::MozDialog:
363 idx = COLOR_3DFACE;
364 break;
365 case ColorID::Accentcolor:
366 aColor = mColorAccent;
367 return NS_OK;
368 case ColorID::Accentcolortext:
369 aColor = mColorAccentText;
370 return NS_OK;
371 case ColorID::MozHeaderbartext:
372 case ColorID::MozHeaderbarinactivetext:
373 case ColorID::MozDialogtext:
374 case ColorID::MozColheadertext:
375 case ColorID::MozColheaderhovertext:
376 idx = COLOR_WINDOWTEXT;
377 break;
378 case ColorID::MozNativehyperlinktext:
379 idx = COLOR_HOTLIGHT;
380 break;
381 case ColorID::Marktext:
382 case ColorID::Mark:
383 case ColorID::SpellCheckerUnderline:
384 aColor = GetStandinForNativeColor(aID, aScheme);
385 return NS_OK;
386 default:
387 idx = COLOR_WINDOW;
388 res = NS_ERROR_FAILURE;
389 break;
392 aColor = GetColorForSysColorIndex(idx);
394 return res;
397 nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) {
398 EnsureInit();
399 nsresult res = NS_OK;
401 switch (aID) {
402 case IntID::ScrollButtonLeftMouseButtonAction:
403 aResult = 0;
404 break;
405 case IntID::ScrollButtonMiddleMouseButtonAction:
406 case IntID::ScrollButtonRightMouseButtonAction:
407 aResult = 3;
408 break;
409 case IntID::CaretBlinkTime:
410 aResult = static_cast<int32_t>(::GetCaretBlinkTime());
411 break;
412 case IntID::CaretBlinkCount: {
413 int32_t timeout = GetSystemParam(SPI_GETCARETTIMEOUT, 5000);
414 auto blinkTime = ::GetCaretBlinkTime();
415 if (timeout <= 0 || blinkTime <= 0) {
416 aResult = -1;
417 break;
419 // 2 * blinkTime because this integer is a full blink cycle.
420 aResult = std::ceil(float(timeout) / (2.0f * float(blinkTime)));
421 break;
424 case IntID::CaretWidth:
425 aResult = 1;
426 break;
427 case IntID::ShowCaretDuringSelection:
428 aResult = 0;
429 break;
430 case IntID::SelectTextfieldsOnKeyFocus:
431 // Select textfield content when focused by kbd
432 // used by EventStateManager::sTextfieldSelectModel
433 aResult = 1;
434 break;
435 case IntID::SubmenuDelay:
436 // This will default to the Windows' default
437 // (400ms) on error.
438 aResult = GetSystemParam(SPI_GETMENUSHOWDELAY, 400);
439 break;
440 case IntID::TooltipDelay:
441 aResult = 500;
442 break;
443 case IntID::MenusCanOverlapOSBar:
444 // we want XUL popups to be able to overlap the task bar.
445 aResult = 1;
446 break;
447 case IntID::DragThresholdX:
448 // The system metric is the number of pixels at which a drag should
449 // start. Our look and feel metric is the number of pixels you can
450 // move before starting a drag, so subtract 1.
451 aResult = ::GetSystemMetrics(SM_CXDRAG) - 1;
452 break;
453 case IntID::DragThresholdY:
454 aResult = ::GetSystemMetrics(SM_CYDRAG) - 1;
455 break;
456 case IntID::UseAccessibilityTheme:
457 // High contrast is a misnomer under Win32 -- any theme can be used with
458 // it, e.g. normal contrast with large fonts, low contrast, etc. The high
459 // contrast flag really means -- use this theme and don't override it.
460 aResult = nsUXThemeData::IsHighContrastOn();
461 break;
462 case IntID::ScrollArrowStyle:
463 aResult = eScrollArrowStyle_Single;
464 break;
465 case IntID::TreeOpenDelay:
466 aResult = 1000;
467 break;
468 case IntID::TreeCloseDelay:
469 aResult = 0;
470 break;
471 case IntID::TreeLazyScrollDelay:
472 aResult = 150;
473 break;
474 case IntID::TreeScrollDelay:
475 aResult = 100;
476 break;
477 case IntID::TreeScrollLinesMax:
478 aResult = 3;
479 break;
480 case IntID::WindowsAccentColorInTitlebar: {
481 aResult = mTitlebarColors.mUseAccent;
482 } break;
483 case IntID::AlertNotificationOrigin:
484 aResult = 0;
486 // Get task bar window handle
487 HWND shellWindow = FindWindowW(L"Shell_TrayWnd", nullptr);
489 if (shellWindow != nullptr) {
490 // Determine position
491 APPBARDATA appBarData;
492 appBarData.hWnd = shellWindow;
493 appBarData.cbSize = sizeof(appBarData);
494 if (SHAppBarMessage(ABM_GETTASKBARPOS, &appBarData)) {
495 // Set alert origin as a bit field - see LookAndFeel.h
496 // 0 represents bottom right, sliding vertically.
497 switch (appBarData.uEdge) {
498 case ABE_LEFT:
499 aResult = NS_ALERT_HORIZONTAL | NS_ALERT_LEFT;
500 break;
501 case ABE_RIGHT:
502 aResult = NS_ALERT_HORIZONTAL;
503 break;
504 case ABE_TOP:
505 aResult = NS_ALERT_TOP;
506 [[fallthrough]];
507 case ABE_BOTTOM:
508 // If the task bar is right-to-left,
509 // move the origin to the left
510 if (::GetWindowLong(shellWindow, GWL_EXSTYLE) & WS_EX_LAYOUTRTL)
511 aResult |= NS_ALERT_LEFT;
512 break;
517 break;
518 case IntID::IMERawInputUnderlineStyle:
519 case IntID::IMEConvertedTextUnderlineStyle:
520 aResult = static_cast<int32_t>(StyleTextDecorationStyle::Dashed);
521 break;
522 case IntID::IMESelectedRawTextUnderlineStyle:
523 case IntID::IMESelectedConvertedTextUnderline:
524 aResult = static_cast<int32_t>(StyleTextDecorationStyle::None);
525 break;
526 case IntID::SpellCheckerUnderlineStyle:
527 aResult = static_cast<int32_t>(StyleTextDecorationStyle::Wavy);
528 break;
529 case IntID::ScrollbarButtonAutoRepeatBehavior:
530 aResult = 0;
531 break;
532 case IntID::SwipeAnimationEnabled:
533 // Forcibly enable the swipe animation on Windows. It doesn't matter on
534 // platforms where "Drag two fingers to scroll" isn't supported since on
535 // the platforms we will never generate any swipe gesture events.
536 aResult = 1;
537 break;
538 case IntID::UseOverlayScrollbars:
539 aResult = WindowsUIUtils::ComputeOverlayScrollbars();
540 break;
541 case IntID::AllowOverlayScrollbarsOverlap:
542 aResult = 0;
543 break;
544 case IntID::ScrollbarDisplayOnMouseMove:
545 aResult = 1;
546 break;
547 case IntID::ScrollbarFadeBeginDelay:
548 aResult = 2500;
549 break;
550 case IntID::ScrollbarFadeDuration:
551 aResult = 350;
552 break;
553 case IntID::ContextMenuOffsetVertical:
554 case IntID::ContextMenuOffsetHorizontal:
555 aResult = 2;
556 break;
557 case IntID::SystemUsesDarkTheme:
558 res = SystemWantsDarkTheme(aResult);
559 break;
560 case IntID::SystemScrollbarSize:
561 aResult = std::max(WinUtils::GetSystemMetricsForDpi(SM_CXVSCROLL, 96),
562 WinUtils::GetSystemMetricsForDpi(SM_CXHSCROLL, 96));
563 break;
564 case IntID::PrefersReducedMotion: {
565 BOOL enable = TRUE;
566 ::SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &enable, 0);
567 aResult = !enable;
568 break;
570 case IntID::PrefersReducedTransparency: {
571 // Prefers reduced transparency if the option for "Transparency Effects"
572 // is disabled
573 aResult = !WindowsUIUtils::ComputeTransparencyEffects();
574 break;
576 case IntID::InvertedColors: {
577 int32_t colorFilter = SystemColorFilter();
579 // Color filter values
580 // 1: Inverted
581 // 2: Grayscale inverted
582 aResult = colorFilter == 1 || colorFilter == 2 ? 1 : 0;
583 break;
585 case IntID::PrimaryPointerCapabilities: {
586 aResult = static_cast<int32_t>(
587 widget::WinUtils::GetPrimaryPointerCapabilities());
588 break;
590 case IntID::AllPointerCapabilities: {
591 aResult =
592 static_cast<int32_t>(widget::WinUtils::GetAllPointerCapabilities());
593 break;
595 case IntID::TouchDeviceSupportPresent:
596 aResult = WinUtils::IsTouchDeviceSupportPresent() ? 1 : 0;
597 break;
598 case IntID::PanelAnimations:
599 aResult = 1;
600 break;
601 case IntID::HideCursorWhileTyping: {
602 BOOL enable = TRUE;
603 ::SystemParametersInfoW(SPI_GETMOUSEVANISH, 0, &enable, 0);
604 aResult = enable;
605 break;
607 default:
608 aResult = 0;
609 res = NS_ERROR_FAILURE;
611 return res;
614 nsresult nsLookAndFeel::NativeGetFloat(FloatID aID, float& aResult) {
615 nsresult res = NS_OK;
617 switch (aID) {
618 case FloatID::IMEUnderlineRelativeSize:
619 aResult = 1.0f;
620 break;
621 case FloatID::SpellCheckerUnderlineRelativeSize:
622 aResult = 1.0f;
623 break;
624 case FloatID::TextScaleFactor:
625 aResult = WindowsUIUtils::ComputeTextScaleFactor();
626 break;
627 default:
628 aResult = -1.0;
629 res = NS_ERROR_FAILURE;
631 return res;
634 LookAndFeelFont nsLookAndFeel::GetLookAndFeelFontInternal(
635 const LOGFONTW& aLogFont, bool aUseShellDlg) {
636 LookAndFeelFont result{};
638 result.haveFont() = false;
640 // Get scaling factor from physical to logical pixels
641 double pixelScale =
642 1.0 / WinUtils::SystemScaleFactor() / LookAndFeel::GetTextScaleFactor();
644 // The lfHeight is in pixels, and it needs to be adjusted for the
645 // device it will be displayed on.
646 // Screens and Printers will differ in DPI
648 // So this accounts for the difference in the DeviceContexts
649 // The pixelScale will typically be 1.0 for the screen
650 // (though larger for hi-dpi screens where the Windows resolution
651 // scale factor is 125% or 150% or even more), and could be
652 // any value when going to a printer, for example pixelScale is
653 // 6.25 when going to a 600dpi printer.
654 float pixelHeight = -aLogFont.lfHeight;
655 if (pixelHeight < 0) {
656 nsAutoFont hFont(::CreateFontIndirectW(&aLogFont));
657 if (!hFont) {
658 return result;
661 nsAutoHDC dc(::GetDC(nullptr));
662 HGDIOBJ hObject = ::SelectObject(dc, hFont);
663 TEXTMETRIC tm;
664 ::GetTextMetrics(dc, &tm);
665 ::SelectObject(dc, hObject);
667 pixelHeight = tm.tmAscent;
670 pixelHeight *= pixelScale;
672 // we have problem on Simplified Chinese system because the system
673 // report the default font size is 8 points. but if we use 8, the text
674 // display very ugly. force it to be at 9 points (12 pixels) on that
675 // system (cp936), but leave other sizes alone.
676 if (pixelHeight < 12 && ::GetACP() == 936) {
677 pixelHeight = 12;
680 result.haveFont() = true;
682 if (aUseShellDlg) {
683 result.name() = u"MS Shell Dlg 2"_ns;
684 } else {
685 result.name() = aLogFont.lfFaceName;
688 result.size() = pixelHeight;
689 result.italic() = !!aLogFont.lfItalic;
690 // FIXME: Other weights?
691 result.weight() =
692 ((aLogFont.lfWeight == FW_BOLD) ? FontWeight::BOLD : FontWeight::NORMAL)
693 .ToFloat();
695 return result;
698 LookAndFeelFont nsLookAndFeel::GetLookAndFeelFont(LookAndFeel::FontID anID) {
699 LookAndFeelFont result{};
701 result.haveFont() = false;
703 // FontID::Icon is handled differently than the others
704 if (anID == LookAndFeel::FontID::Icon) {
705 LOGFONTW logFont;
706 if (::SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(logFont),
707 (PVOID)&logFont, 0)) {
708 result = GetLookAndFeelFontInternal(logFont, false);
710 return result;
713 NONCLIENTMETRICSW ncm;
714 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
715 if (!::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm),
716 (PVOID)&ncm, 0)) {
717 return result;
720 switch (anID) {
721 case LookAndFeel::FontID::Menu:
722 case LookAndFeel::FontID::MozPullDownMenu:
723 result = GetLookAndFeelFontInternal(ncm.lfMenuFont, false);
724 break;
725 case LookAndFeel::FontID::Caption:
726 result = GetLookAndFeelFontInternal(ncm.lfCaptionFont, false);
727 break;
728 case LookAndFeel::FontID::SmallCaption:
729 result = GetLookAndFeelFontInternal(ncm.lfSmCaptionFont, false);
730 break;
731 case LookAndFeel::FontID::StatusBar:
732 result = GetLookAndFeelFontInternal(ncm.lfStatusFont, false);
733 break;
734 case LookAndFeel::FontID::MozButton:
735 case LookAndFeel::FontID::MozField:
736 case LookAndFeel::FontID::MozList:
737 // XXX It's not clear to me whether this is exactly the right
738 // set of LookAndFeel values to map to the dialog font; we may
739 // want to add or remove cases here after reviewing the visual
740 // results under various Windows versions.
741 result = GetLookAndFeelFontInternal(ncm.lfMessageFont, true);
742 break;
743 default:
744 result = GetLookAndFeelFontInternal(ncm.lfMessageFont, false);
745 break;
748 return result;
751 bool nsLookAndFeel::NativeGetFont(LookAndFeel::FontID anID, nsString& aFontName,
752 gfxFontStyle& aFontStyle) {
753 LookAndFeelFont font = GetLookAndFeelFont(anID);
754 return LookAndFeelFontToStyle(font, aFontName, aFontStyle);
757 /* virtual */
758 char16_t nsLookAndFeel::GetPasswordCharacterImpl() {
759 #define UNICODE_BLACK_CIRCLE_CHAR 0x25cf
760 return UNICODE_BLACK_CIRCLE_CHAR;
763 static nscolor GetAccentColorText(const nscolor aAccentColor) {
764 // We want the color that we return for text that will be drawn over
765 // a background that has the accent color to have good contrast with
766 // the accent color. Windows itself uses either white or black text
767 // depending on how light or dark the accent color is. We do the same
768 // here based on the luminance of the accent color with a threshhold
769 // value. This algorithm should match what Windows does. It comes from:
771 // https://docs.microsoft.com/en-us/windows/uwp/style/color
772 float luminance = (NS_GET_R(aAccentColor) * 2 + NS_GET_G(aAccentColor) * 5 +
773 NS_GET_B(aAccentColor)) /
775 return luminance <= 128 ? NS_RGB(255, 255, 255) : NS_RGB(0, 0, 0);
778 static Maybe<nscolor> GetAccentColorText(const Maybe<nscolor>& aAccentColor) {
779 if (!aAccentColor) {
780 return Nothing();
782 return Some(GetAccentColorText(*aAccentColor));
785 nscolor nsLookAndFeel::GetColorForSysColorIndex(int index) {
786 MOZ_ASSERT(index >= SYS_COLOR_MIN && index <= SYS_COLOR_MAX);
787 return mSysColorTable[index - SYS_COLOR_MIN];
790 auto nsLookAndFeel::ComputeTitlebarColors() -> TitlebarColors {
791 TitlebarColors result;
793 // Start with the native / non-accent-in-titlebar colors.
794 result.mActiveLight = {GetColorForSysColorIndex(COLOR_ACTIVECAPTION),
795 GetColorForSysColorIndex(COLOR_CAPTIONTEXT),
796 GetColorForSysColorIndex(COLOR_ACTIVEBORDER)};
798 result.mInactiveLight = {GetColorForSysColorIndex(COLOR_INACTIVECAPTION),
799 GetColorForSysColorIndex(COLOR_INACTIVECAPTIONTEXT),
800 GetColorForSysColorIndex(COLOR_INACTIVEBORDER)};
802 if (!nsUXThemeData::IsHighContrastOn()) {
803 // Use our non-native colors.
804 result.mActiveLight = {
805 GetStandinForNativeColor(ColorID::Activecaption, ColorScheme::Light),
806 GetStandinForNativeColor(ColorID::Captiontext, ColorScheme::Light),
807 GetStandinForNativeColor(ColorID::Activeborder, ColorScheme::Light)};
808 result.mInactiveLight = {
809 GetStandinForNativeColor(ColorID::Inactivecaption, ColorScheme::Light),
810 GetStandinForNativeColor(ColorID::Inactivecaptiontext,
811 ColorScheme::Light),
812 GetStandinForNativeColor(ColorID::Inactiveborder, ColorScheme::Light)};
815 // Our dark colors are always non-native.
816 result.mActiveDark = {*GenericDarkColor(ColorID::Activecaption),
817 *GenericDarkColor(ColorID::Captiontext),
818 *GenericDarkColor(ColorID::Activeborder)};
819 result.mInactiveDark = {*GenericDarkColor(ColorID::Inactivecaption),
820 *GenericDarkColor(ColorID::Inactivecaptiontext),
821 *GenericDarkColor(ColorID::Inactiveborder)};
823 nsCOMPtr<nsIWindowsRegKey> dwmKey =
824 do_CreateInstance("@mozilla.org/windows-registry-key;1");
825 if (!dwmKey) {
826 return result;
828 // TODO(bug 1825241): Somehow get notified when this changes? Hopefully the
829 // sys color notification is enough.
830 nsresult rv = dwmKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
831 u"SOFTWARE\\Microsoft\\Windows\\DWM"_ns,
832 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
833 NS_ENSURE_SUCCESS(rv, result);
835 auto close = mozilla::MakeScopeExit([&] { dwmKey->Close(); });
837 auto ReadColor = [&](const nsAString& aName) -> Maybe<nscolor> {
838 uint32_t color;
839 if (NS_SUCCEEDED(dwmKey->ReadIntValue(aName, &color))) {
840 // The order of the color components in the DWORD stored in the registry
841 // happens to be the same order as we store the components in nscolor
842 // so we can just assign directly here.
843 return Some(color);
845 return Nothing();
848 result.mAccent = ReadColor(u"AccentColor"_ns);
849 result.mAccentText = GetAccentColorText(result.mAccent);
851 if (!result.mAccent) {
852 return result;
855 result.mAccentInactive = ReadColor(u"AccentColorInactive"_ns);
856 result.mAccentInactiveText = GetAccentColorText(result.mAccentInactive);
858 // The ColorPrevalence value is set to 1 when the "Show color on title bar"
859 // setting in the Color section of Window's Personalization settings is
860 // turned on.
861 uint32_t prevalence = 0;
862 result.mUseAccent =
863 NS_SUCCEEDED(dwmKey->ReadIntValue(u"ColorPrevalence"_ns, &prevalence)) &&
864 prevalence == 1;
865 if (!result.mUseAccent) {
866 return result;
869 // TODO(emilio): Consider reading ColorizationColorBalance to compute a
870 // more correct border color, see [1]. Though for opaque accent colors this
871 // isn't needed.
873 // [1]:
874 // https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/color/win/accent_color_observer.cc;l=42;drc=9d4eb7ed25296abba8fd525a6bdd0fdbf4bcdd9f
875 result.mActiveDark.mBorder = result.mActiveLight.mBorder = *result.mAccent;
876 result.mInactiveDark.mBorder = result.mInactiveLight.mBorder =
877 result.mAccentInactive.valueOr(NS_RGB(57, 57, 57));
878 result.mActiveLight.mBg = result.mActiveDark.mBg = *result.mAccent;
879 result.mActiveLight.mFg = result.mActiveDark.mFg = *result.mAccentText;
880 if (result.mAccentInactive) {
881 result.mInactiveLight.mBg = result.mInactiveDark.mBg =
882 *result.mAccentInactive;
883 result.mInactiveLight.mFg = result.mInactiveDark.mFg =
884 *result.mAccentInactiveText;
885 } else {
886 // The 153 matches the .6 opacity the front-end uses, which was calculated
887 // to match the opacity change of Windows Explorer titlebar text change
888 // for inactive windows.
889 constexpr uint8_t kTextAlpha = 153;
890 // This is hand-picked to .8 to change the accent color a bit but not too
891 // much.
892 constexpr uint8_t kBgAlpha = 208;
893 const auto BlendWithAlpha = [](nscolor aBg, nscolor aFg,
894 uint8_t aAlpha) -> nscolor {
895 return NS_ComposeColors(
896 aBg, NS_RGBA(NS_GET_R(aFg), NS_GET_G(aFg), NS_GET_B(aFg), aAlpha));
899 result.mInactiveLight.mBg =
900 BlendWithAlpha(NS_RGB(255, 255, 255), *result.mAccent, kBgAlpha);
901 result.mInactiveLight.mFg =
902 BlendWithAlpha(*result.mAccent, *result.mAccentText, kTextAlpha);
904 result.mInactiveDark.mBg =
905 BlendWithAlpha(NS_RGB(0, 0, 0), *result.mAccent, kBgAlpha);
906 result.mInactiveDark.mFg =
907 BlendWithAlpha(*result.mAccent, *result.mAccentText, kTextAlpha);
909 return result;
912 void nsLookAndFeel::EnsureInit() {
913 if (mInitialized) {
914 return;
916 mInitialized = true;
918 mColorMenuHoverText =
919 ::GetColorFromTheme(eUXMenu, MENU_POPUPITEM, MPI_HOT, TMT_TEXTCOLOR);
921 // Fill out the sys color table.
922 for (int i = SYS_COLOR_MIN; i <= SYS_COLOR_MAX; ++i) {
923 mSysColorTable[i - SYS_COLOR_MIN] = [&] {
924 if (auto c = WindowsUIUtils::GetSystemColor(ColorScheme::Light, i)) {
925 return *c;
927 DWORD color = ::GetSysColor(i);
928 return COLOREF_2_NSRGB(color);
929 }();
932 mDarkHighlight =
933 WindowsUIUtils::GetSystemColor(ColorScheme::Dark, COLOR_HIGHLIGHT);
934 mDarkHighlightText =
935 WindowsUIUtils::GetSystemColor(ColorScheme::Dark, COLOR_HIGHLIGHTTEXT);
937 mTitlebarColors = ComputeTitlebarColors();
939 mColorAccent = [&] {
940 if (auto accent = WindowsUIUtils::GetAccentColor()) {
941 return *accent;
943 // Try the titlebar accent as a fallback.
944 if (mTitlebarColors.mAccent) {
945 return *mTitlebarColors.mAccent;
947 // Seems to be the default color (hardcoded because of bug 1065998)
948 return NS_RGB(0, 120, 215);
949 }();
950 mColorAccentText = GetAccentColorText(mColorAccent);
951 RecordTelemetry();