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"
10 #include "nsStyleConsts.h"
11 #include "nsUXThemeData.h"
12 #include "nsUXThemeConstants.h"
13 #include "nsWindowsHelpers.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
) {
28 HRESULT hr
= GetThemeColor(nsUXThemeData::GetTheme(cls
), aPart
, aState
,
31 return Some(COLOREF_2_NSRGB(color
));
36 static int32_t GetSystemParam(long flag
, int32_t def
) {
38 return ::SystemParametersInfo(flag
, 0, &value
, 0) ? value
: def
;
41 static nsresult
SystemWantsDarkTheme(int32_t& darkThemeEnabled
) {
43 nsCOMPtr
<nsIWindowsRegKey
> personalizeKey
=
44 do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv
);
45 if (NS_WARN_IF(NS_FAILED(rv
))) {
49 rv
= personalizeKey
->Open(
50 nsIWindowsRegKey::ROOT_KEY_CURRENT_USER
,
52 u
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"),
53 nsIWindowsRegKey::ACCESS_QUERY_VALUE
);
58 uint32_t lightThemeEnabled
;
60 personalizeKey
->ReadIntValue(u
"AppsUseLightTheme"_ns
, &lightThemeEnabled
);
61 if (NS_SUCCEEDED(rv
)) {
62 darkThemeEnabled
= !lightThemeEnabled
;
68 static int32_t SystemColorFilter() {
70 nsCOMPtr
<nsIWindowsRegKey
> colorFilteringKey
=
71 do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv
);
72 if (NS_WARN_IF(NS_FAILED(rv
))) {
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
);
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
89 rv
= colorFilteringKey
->ReadIntValue(u
"Active"_ns
, &active
);
90 if (NS_FAILED(rv
) || active
== 0) {
94 // The FilterType value is set to whichever filter is enabled.
96 rv
= colorFilteringKey
->ReadIntValue(u
"FilterType"_ns
, &filterType
);
97 if (NS_SUCCEEDED(rv
)) {
104 nsLookAndFeel::nsLookAndFeel() {
105 mozilla::Telemetry::Accumulate(mozilla::Telemetry::TOUCH_ENABLED_DEVICE
,
106 WinUtils::IsTouchDeviceSupportPresent());
109 nsLookAndFeel::~nsLookAndFeel() = default;
111 void nsLookAndFeel::NativeInit() { EnsureInit(); }
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
,
128 auto IsHighlightColor
= [&] {
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
136 return aScheme
!= ColorScheme::Dark
|| mDarkHighlight
;
137 case ColorID::IMESelectedRawTextBackground
:
138 case ColorID::IMESelectedConvertedTextBackground
:
145 auto IsHighlightTextColor
= [&] {
147 case ColorID::MozMenubarhovertext
:
148 if (UseNonNativeMenuColors(aScheme
)) {
152 case ColorID::MozMenuhovertext
:
153 if (UseNonNativeMenuColors(aScheme
)) {
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
161 return aScheme
!= ColorScheme::Dark
|| mDarkHighlightText
;
162 case ColorID::IMESelectedRawTextForeground
:
163 case ColorID::IMESelectedConvertedTextForeground
:
170 if (IsHighlightColor()) {
171 if (aScheme
== ColorScheme::Dark
&& mDarkHighlight
) {
172 aColor
= *mDarkHighlight
;
174 aColor
= GetColorForSysColorIndex(COLOR_HIGHLIGHT
);
179 if (IsHighlightTextColor()) {
180 if (aScheme
== ColorScheme::Dark
&& mDarkHighlightText
) {
181 aColor
= *mDarkHighlightText
;
183 aColor
= GetColorForSysColorIndex(COLOR_HIGHLIGHTTEXT
);
188 // Titlebar colors are color-scheme aware.
190 case ColorID::Activecaption
:
191 aColor
= mTitlebarColors
.Get(aScheme
, true).mBg
;
193 case ColorID::Captiontext
:
194 aColor
= mTitlebarColors
.Get(aScheme
, true).mFg
;
196 case ColorID::Activeborder
:
197 aColor
= mTitlebarColors
.Get(aScheme
, true).mBorder
;
199 case ColorID::Inactivecaption
:
200 aColor
= mTitlebarColors
.Get(aScheme
, false).mBg
;
202 case ColorID::Inactivecaptiontext
:
203 aColor
= mTitlebarColors
.Get(aScheme
, false).mFg
;
205 case ColorID::Inactiveborder
:
206 aColor
= mTitlebarColors
.Get(aScheme
, false).mBorder
;
212 if (aScheme
== ColorScheme::Dark
) {
213 if (auto color
= GenericDarkColor(aID
)) {
219 static constexpr auto kNonNativeMenuText
= NS_RGB(0x15, 0x14, 0x1a);
220 nsresult res
= NS_OK
;
223 case ColorID::IMERawInputBackground
:
224 case ColorID::IMEConvertedTextBackground
:
225 aColor
= NS_TRANSPARENT
;
227 case ColorID::IMERawInputForeground
:
228 case ColorID::IMEConvertedTextForeground
:
229 aColor
= NS_SAME_AS_FOREGROUND_COLOR
;
231 case ColorID::IMERawInputUnderline
:
232 case ColorID::IMEConvertedTextUnderline
:
233 aColor
= NS_SAME_AS_FOREGROUND_COLOR
;
235 case ColorID::IMESelectedRawTextUnderline
:
236 case ColorID::IMESelectedConvertedTextUnderline
:
237 aColor
= NS_TRANSPARENT
;
240 // New CSS 2 Color definitions
241 case ColorID::Appworkspace
:
242 idx
= COLOR_APPWORKSPACE
;
244 case ColorID::Background
:
245 idx
= COLOR_BACKGROUND
;
247 case ColorID::Buttonface
:
248 case ColorID::MozButtonhoverface
:
249 case ColorID::MozButtonactiveface
:
250 case ColorID::MozButtondisabledface
:
253 case ColorID::Buttonhighlight
:
254 idx
= COLOR_BTNHIGHLIGHT
;
256 case ColorID::Buttonshadow
:
257 idx
= COLOR_BTNSHADOW
;
259 case ColorID::Buttontext
:
260 case ColorID::MozButtonhovertext
:
261 case ColorID::MozButtonactivetext
:
264 case ColorID::MozCellhighlighttext
:
265 aColor
= NS_RGB(0, 0, 0);
267 case ColorID::MozCellhighlight
:
268 aColor
= NS_RGB(206, 206, 206);
270 case ColorID::Graytext
:
271 idx
= COLOR_GRAYTEXT
;
273 case ColorID::MozMenubarhovertext
:
274 if (UseNonNativeMenuColors(aScheme
)) {
275 aColor
= kNonNativeMenuText
;
279 case ColorID::MozMenuhovertext
:
280 if (UseNonNativeMenuColors(aScheme
)) {
281 aColor
= kNonNativeMenuText
;
284 if (mColorMenuHoverText
) {
285 aColor
= *mColorMenuHoverText
;
288 idx
= COLOR_HIGHLIGHTTEXT
;
290 case ColorID::MozMenuhover
:
291 MOZ_ASSERT(UseNonNativeMenuColors(aScheme
));
292 aColor
= NS_RGB(0xe0, 0xe0, 0xe6);
294 case ColorID::MozMenuhoverdisabled
:
295 if (UseNonNativeMenuColors(aScheme
)) {
296 aColor
= NS_RGB(0xf0, 0xf0, 0xf3);
299 aColor
= NS_TRANSPARENT
;
301 case ColorID::Infobackground
:
304 case ColorID::Infotext
:
305 idx
= COLOR_INFOTEXT
;
308 if (UseNonNativeMenuColors(aScheme
)) {
309 aColor
= NS_RGB(0xf9, 0xf9, 0xfb);
314 case ColorID::Menutext
:
315 if (UseNonNativeMenuColors(aScheme
)) {
316 aColor
= kNonNativeMenuText
;
319 idx
= COLOR_MENUTEXT
;
321 case ColorID::Scrollbar
:
322 idx
= COLOR_SCROLLBAR
;
324 case ColorID::Threeddarkshadow
:
325 idx
= COLOR_3DDKSHADOW
;
327 case ColorID::Threedface
:
330 case ColorID::Threedhighlight
:
331 idx
= COLOR_3DHIGHLIGHT
;
333 case ColorID::Threedlightshadow
:
334 case ColorID::Buttonborder
:
335 case ColorID::MozDisabledfield
:
338 case ColorID::Threedshadow
:
339 idx
= COLOR_3DSHADOW
;
341 case ColorID::Window
:
344 case ColorID::Windowframe
:
345 idx
= COLOR_WINDOWFRAME
;
347 case ColorID::Windowtext
:
348 idx
= COLOR_WINDOWTEXT
;
350 case ColorID::MozEventreerow
:
351 case ColorID::MozOddtreerow
:
353 case ColorID::MozCombobox
:
356 case ColorID::Fieldtext
:
357 case ColorID::MozComboboxtext
:
358 idx
= COLOR_WINDOWTEXT
;
360 case ColorID::MozHeaderbar
:
361 case ColorID::MozHeaderbarinactive
:
362 case ColorID::MozDialog
:
365 case ColorID::Accentcolor
:
366 aColor
= mColorAccent
;
368 case ColorID::Accentcolortext
:
369 aColor
= mColorAccentText
;
371 case ColorID::MozHeaderbartext
:
372 case ColorID::MozHeaderbarinactivetext
:
373 case ColorID::MozDialogtext
:
374 case ColorID::MozColheadertext
:
375 case ColorID::MozColheaderhovertext
:
376 idx
= COLOR_WINDOWTEXT
;
378 case ColorID::MozNativehyperlinktext
:
379 idx
= COLOR_HOTLIGHT
;
381 case ColorID::Marktext
:
383 case ColorID::SpellCheckerUnderline
:
384 aColor
= GetStandinForNativeColor(aID
, aScheme
);
388 res
= NS_ERROR_FAILURE
;
392 aColor
= GetColorForSysColorIndex(idx
);
397 nsresult
nsLookAndFeel::NativeGetInt(IntID aID
, int32_t& aResult
) {
399 nsresult res
= NS_OK
;
402 case IntID::ScrollButtonLeftMouseButtonAction
:
405 case IntID::ScrollButtonMiddleMouseButtonAction
:
406 case IntID::ScrollButtonRightMouseButtonAction
:
409 case IntID::CaretBlinkTime
:
410 aResult
= static_cast<int32_t>(::GetCaretBlinkTime());
412 case IntID::CaretBlinkCount
: {
413 int32_t timeout
= GetSystemParam(SPI_GETCARETTIMEOUT
, 5000);
414 auto blinkTime
= ::GetCaretBlinkTime();
415 if (timeout
<= 0 || blinkTime
<= 0) {
419 // 2 * blinkTime because this integer is a full blink cycle.
420 aResult
= std::ceil(float(timeout
) / (2.0f
* float(blinkTime
)));
424 case IntID::CaretWidth
:
427 case IntID::ShowCaretDuringSelection
:
430 case IntID::SelectTextfieldsOnKeyFocus
:
431 // Select textfield content when focused by kbd
432 // used by EventStateManager::sTextfieldSelectModel
435 case IntID::SubmenuDelay
:
436 // This will default to the Windows' default
438 aResult
= GetSystemParam(SPI_GETMENUSHOWDELAY
, 400);
440 case IntID::TooltipDelay
:
443 case IntID::MenusCanOverlapOSBar
:
444 // we want XUL popups to be able to overlap the task bar.
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;
453 case IntID::DragThresholdY
:
454 aResult
= ::GetSystemMetrics(SM_CYDRAG
) - 1;
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();
462 case IntID::ScrollArrowStyle
:
463 aResult
= eScrollArrowStyle_Single
;
465 case IntID::TreeOpenDelay
:
468 case IntID::TreeCloseDelay
:
471 case IntID::TreeLazyScrollDelay
:
474 case IntID::TreeScrollDelay
:
477 case IntID::TreeScrollLinesMax
:
480 case IntID::WindowsAccentColorInTitlebar
: {
481 aResult
= mTitlebarColors
.mUseAccent
;
483 case IntID::AlertNotificationOrigin
:
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
) {
499 aResult
= NS_ALERT_HORIZONTAL
| NS_ALERT_LEFT
;
502 aResult
= NS_ALERT_HORIZONTAL
;
505 aResult
= NS_ALERT_TOP
;
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
;
518 case IntID::IMERawInputUnderlineStyle
:
519 case IntID::IMEConvertedTextUnderlineStyle
:
520 aResult
= static_cast<int32_t>(StyleTextDecorationStyle::Dashed
);
522 case IntID::IMESelectedRawTextUnderlineStyle
:
523 case IntID::IMESelectedConvertedTextUnderline
:
524 aResult
= static_cast<int32_t>(StyleTextDecorationStyle::None
);
526 case IntID::SpellCheckerUnderlineStyle
:
527 aResult
= static_cast<int32_t>(StyleTextDecorationStyle::Wavy
);
529 case IntID::ScrollbarButtonAutoRepeatBehavior
:
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.
538 case IntID::UseOverlayScrollbars
:
539 aResult
= WindowsUIUtils::ComputeOverlayScrollbars();
541 case IntID::AllowOverlayScrollbarsOverlap
:
544 case IntID::ScrollbarDisplayOnMouseMove
:
547 case IntID::ScrollbarFadeBeginDelay
:
550 case IntID::ScrollbarFadeDuration
:
553 case IntID::ContextMenuOffsetVertical
:
554 case IntID::ContextMenuOffsetHorizontal
:
557 case IntID::SystemUsesDarkTheme
:
558 res
= SystemWantsDarkTheme(aResult
);
560 case IntID::SystemScrollbarSize
:
561 aResult
= std::max(WinUtils::GetSystemMetricsForDpi(SM_CXVSCROLL
, 96),
562 WinUtils::GetSystemMetricsForDpi(SM_CXHSCROLL
, 96));
564 case IntID::PrefersReducedMotion
: {
566 ::SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION
, 0, &enable
, 0);
570 case IntID::PrefersReducedTransparency
: {
571 // Prefers reduced transparency if the option for "Transparency Effects"
573 aResult
= !WindowsUIUtils::ComputeTransparencyEffects();
576 case IntID::InvertedColors
: {
577 int32_t colorFilter
= SystemColorFilter();
579 // Color filter values
581 // 2: Grayscale inverted
582 aResult
= colorFilter
== 1 || colorFilter
== 2 ? 1 : 0;
585 case IntID::PrimaryPointerCapabilities
: {
586 aResult
= static_cast<int32_t>(
587 widget::WinUtils::GetPrimaryPointerCapabilities());
590 case IntID::AllPointerCapabilities
: {
592 static_cast<int32_t>(widget::WinUtils::GetAllPointerCapabilities());
595 case IntID::TouchDeviceSupportPresent
:
596 aResult
= WinUtils::IsTouchDeviceSupportPresent() ? 1 : 0;
598 case IntID::PanelAnimations
:
601 case IntID::HideCursorWhileTyping
: {
603 ::SystemParametersInfoW(SPI_GETMOUSEVANISH
, 0, &enable
, 0);
609 res
= NS_ERROR_FAILURE
;
614 nsresult
nsLookAndFeel::NativeGetFloat(FloatID aID
, float& aResult
) {
615 nsresult res
= NS_OK
;
618 case FloatID::IMEUnderlineRelativeSize
:
621 case FloatID::SpellCheckerUnderlineRelativeSize
:
624 case FloatID::TextScaleFactor
:
625 aResult
= WindowsUIUtils::ComputeTextScaleFactor();
629 res
= NS_ERROR_FAILURE
;
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
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
));
661 nsAutoHDC
dc(::GetDC(nullptr));
662 HGDIOBJ hObject
= ::SelectObject(dc
, hFont
);
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) {
680 result
.haveFont() = true;
683 result
.name() = u
"MS Shell Dlg 2"_ns
;
685 result
.name() = aLogFont
.lfFaceName
;
688 result
.size() = pixelHeight
;
689 result
.italic() = !!aLogFont
.lfItalic
;
690 // FIXME: Other weights?
692 ((aLogFont
.lfWeight
== FW_BOLD
) ? FontWeight::BOLD
: FontWeight::NORMAL
)
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
) {
706 if (::SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, sizeof(logFont
),
707 (PVOID
)&logFont
, 0)) {
708 result
= GetLookAndFeelFontInternal(logFont
, false);
713 NONCLIENTMETRICSW ncm
;
714 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
715 if (!::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
),
721 case LookAndFeel::FontID::Menu
:
722 case LookAndFeel::FontID::MozPullDownMenu
:
723 result
= GetLookAndFeelFontInternal(ncm
.lfMenuFont
, false);
725 case LookAndFeel::FontID::Caption
:
726 result
= GetLookAndFeelFontInternal(ncm
.lfCaptionFont
, false);
728 case LookAndFeel::FontID::SmallCaption
:
729 result
= GetLookAndFeelFontInternal(ncm
.lfSmCaptionFont
, false);
731 case LookAndFeel::FontID::StatusBar
:
732 result
= GetLookAndFeelFontInternal(ncm
.lfStatusFont
, false);
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);
744 result
= GetLookAndFeelFontInternal(ncm
.lfMessageFont
, false);
751 bool nsLookAndFeel::NativeGetFont(LookAndFeel::FontID anID
, nsString
& aFontName
,
752 gfxFontStyle
& aFontStyle
) {
753 LookAndFeelFont font
= GetLookAndFeelFont(anID
);
754 return LookAndFeelFontToStyle(font
, aFontName
, aFontStyle
);
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
) {
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
,
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");
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
> {
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.
848 result
.mAccent
= ReadColor(u
"AccentColor"_ns
);
849 result
.mAccentText
= GetAccentColorText(result
.mAccent
);
851 if (!result
.mAccent
) {
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
861 uint32_t prevalence
= 0;
863 NS_SUCCEEDED(dwmKey
->ReadIntValue(u
"ColorPrevalence"_ns
, &prevalence
)) &&
865 if (!result
.mUseAccent
) {
869 // TODO(emilio): Consider reading ColorizationColorBalance to compute a
870 // more correct border color, see [1]. Though for opaque accent colors this
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
;
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
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
);
912 void nsLookAndFeel::EnsureInit() {
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
)) {
927 DWORD color
= ::GetSysColor(i
);
928 return COLOREF_2_NSRGB(color
);
933 WindowsUIUtils::GetSystemColor(ColorScheme::Dark
, COLOR_HIGHLIGHT
);
935 WindowsUIUtils::GetSystemColor(ColorScheme::Dark
, COLOR_HIGHLIGHTTEXT
);
937 mTitlebarColors
= ComputeTitlebarColors();
940 if (auto accent
= WindowsUIUtils::GetAccentColor()) {
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);
950 mColorAccentText
= GetAccentColorText(mColorAccent
);