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 "mozilla/widget/WinRegistry.h"
20 using namespace mozilla
;
21 using namespace mozilla::widget
;
23 static Maybe
<nscolor
> GetColorFromTheme(nsUXThemeClass cls
, int32_t aPart
,
24 int32_t aState
, int32_t aPropId
) {
26 HRESULT hr
= GetThemeColor(nsUXThemeData::GetTheme(cls
), aPart
, aState
,
29 return Some(COLOREF_2_NSRGB(color
));
34 static int32_t GetSystemParam(long flag
, int32_t def
) {
36 return ::SystemParametersInfo(flag
, 0, &value
, 0) ? value
: def
;
39 static int32_t GetTooltipOffsetVertical() {
40 static constexpr DWORD kDefaultCursorSize
= 32;
41 const DWORD cursorSize
=
42 GetSystemParam(MOZ_SPI_CURSORSIZE
, kDefaultCursorSize
);
43 if (cursorSize
== kDefaultCursorSize
) {
44 return LookAndFeel::kDefaultTooltipOffset
;
46 return std::ceilf(float(LookAndFeel::kDefaultTooltipOffset
) *
47 float(cursorSize
) / float(kDefaultCursorSize
));
50 static bool SystemWantsDarkTheme() {
51 if (nsUXThemeData::IsHighContrastOn()) {
52 return LookAndFeel::IsDarkColor(
53 LookAndFeel::Color(StyleSystemColor::Window
, ColorScheme::Light
,
54 LookAndFeel::UseStandins::No
));
59 u
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"_ns
,
60 WinRegistry::KeyMode::QueryValue
);
61 if (NS_WARN_IF(!key
)) {
64 uint32_t light
= key
.GetValueAsDword(u
"AppsUseLightTheme"_ns
).valueOr(1);
68 uint32_t nsLookAndFeel::SystemColorFilter() {
69 if (NS_WARN_IF(!mColorFilterWatcher
)) {
73 const auto& key
= mColorFilterWatcher
->GetKey();
74 if (!key
.GetValueAsDword(u
"Active"_ns
).valueOr(0)) {
77 return key
.GetValueAsDword(u
"FilterType"_ns
).valueOr(0);
80 nsLookAndFeel::nsLookAndFeel() {
81 mozilla::Telemetry::Accumulate(mozilla::Telemetry::TOUCH_ENABLED_DEVICE
,
82 WinUtils::IsTouchDeviceSupportPresent());
85 nsLookAndFeel::~nsLookAndFeel() = default;
87 void nsLookAndFeel::NativeInit() { EnsureInit(); }
90 void nsLookAndFeel::RefreshImpl() {
91 mInitialized
= false; // Fetch system colors next time they're used.
92 nsXPLookAndFeel::RefreshImpl();
95 static bool UseNonNativeMenuColors(ColorScheme aScheme
) {
96 return !LookAndFeel::GetInt(LookAndFeel::IntID::UseAccessibilityTheme
) ||
97 aScheme
== ColorScheme::Dark
;
100 nsresult
nsLookAndFeel::NativeGetColor(ColorID aID
, ColorScheme aScheme
,
104 auto IsHighlightColor
= [&] {
106 case ColorID::MozMenuhover
:
107 return !UseNonNativeMenuColors(aScheme
);
108 case ColorID::Highlight
:
109 case ColorID::Selecteditem
:
110 // We prefer the generic dark selection color if we don't have an
112 return aScheme
!= ColorScheme::Dark
|| mDarkHighlight
;
113 case ColorID::IMESelectedRawTextBackground
:
114 case ColorID::IMESelectedConvertedTextBackground
:
121 auto IsHighlightTextColor
= [&] {
123 case ColorID::MozMenubarhovertext
:
124 if (UseNonNativeMenuColors(aScheme
)) {
128 case ColorID::MozMenuhovertext
:
129 if (UseNonNativeMenuColors(aScheme
)) {
132 return !mColorMenuHoverText
;
133 case ColorID::Highlighttext
:
134 case ColorID::Selecteditemtext
:
135 // We prefer the generic dark selection color if we don't have an
137 return aScheme
!= ColorScheme::Dark
|| mDarkHighlightText
;
138 case ColorID::IMESelectedRawTextForeground
:
139 case ColorID::IMESelectedConvertedTextForeground
:
146 if (IsHighlightColor()) {
147 if (aScheme
== ColorScheme::Dark
&& mDarkHighlight
) {
148 aColor
= *mDarkHighlight
;
150 aColor
= GetColorForSysColorIndex(COLOR_HIGHLIGHT
);
155 if (IsHighlightTextColor()) {
156 if (aScheme
== ColorScheme::Dark
&& mDarkHighlightText
) {
157 aColor
= *mDarkHighlightText
;
159 aColor
= GetColorForSysColorIndex(COLOR_HIGHLIGHTTEXT
);
164 // Titlebar colors are color-scheme aware.
166 case ColorID::Activecaption
:
167 aColor
= mTitlebarColors
.Get(aScheme
, true).mBg
;
169 case ColorID::Captiontext
:
170 aColor
= mTitlebarColors
.Get(aScheme
, true).mFg
;
172 case ColorID::Activeborder
:
173 aColor
= mTitlebarColors
.Get(aScheme
, true).mBorder
;
175 case ColorID::Inactivecaption
:
176 aColor
= mTitlebarColors
.Get(aScheme
, false).mBg
;
178 case ColorID::Inactivecaptiontext
:
179 aColor
= mTitlebarColors
.Get(aScheme
, false).mFg
;
181 case ColorID::Inactiveborder
:
182 aColor
= mTitlebarColors
.Get(aScheme
, false).mBorder
;
188 if (aScheme
== ColorScheme::Dark
) {
189 if (auto color
= GenericDarkColor(aID
)) {
195 static constexpr auto kNonNativeMenuText
= NS_RGB(0x15, 0x14, 0x1a);
196 nsresult res
= NS_OK
;
199 case ColorID::IMERawInputBackground
:
200 case ColorID::IMEConvertedTextBackground
:
201 aColor
= NS_TRANSPARENT
;
203 case ColorID::IMERawInputForeground
:
204 case ColorID::IMEConvertedTextForeground
:
205 aColor
= NS_SAME_AS_FOREGROUND_COLOR
;
207 case ColorID::IMERawInputUnderline
:
208 case ColorID::IMEConvertedTextUnderline
:
209 aColor
= NS_SAME_AS_FOREGROUND_COLOR
;
211 case ColorID::IMESelectedRawTextUnderline
:
212 case ColorID::IMESelectedConvertedTextUnderline
:
213 aColor
= NS_TRANSPARENT
;
216 // New CSS 2 Color definitions
217 case ColorID::Appworkspace
:
218 idx
= COLOR_APPWORKSPACE
;
220 case ColorID::Background
:
221 idx
= COLOR_BACKGROUND
;
223 case ColorID::Buttonface
:
224 case ColorID::MozButtonhoverface
:
225 case ColorID::MozButtonactiveface
:
226 case ColorID::MozButtondisabledface
:
227 case ColorID::MozColheader
:
228 case ColorID::MozColheaderhover
:
229 case ColorID::MozColheaderactive
:
232 case ColorID::Buttonhighlight
:
233 idx
= COLOR_BTNHIGHLIGHT
;
235 case ColorID::Buttonshadow
:
236 idx
= COLOR_BTNSHADOW
;
238 case ColorID::Buttontext
:
239 case ColorID::MozButtonhovertext
:
240 case ColorID::MozButtonactivetext
:
243 case ColorID::MozCellhighlighttext
:
244 aColor
= NS_RGB(0, 0, 0);
246 case ColorID::MozCellhighlight
:
247 aColor
= NS_RGB(206, 206, 206);
249 case ColorID::Graytext
:
250 idx
= COLOR_GRAYTEXT
;
252 case ColorID::MozMenubarhovertext
:
253 if (UseNonNativeMenuColors(aScheme
)) {
254 aColor
= kNonNativeMenuText
;
258 case ColorID::MozMenuhovertext
:
259 if (UseNonNativeMenuColors(aScheme
)) {
260 aColor
= kNonNativeMenuText
;
263 if (mColorMenuHoverText
) {
264 aColor
= *mColorMenuHoverText
;
267 idx
= COLOR_HIGHLIGHTTEXT
;
269 case ColorID::MozMenuhover
:
270 MOZ_ASSERT(UseNonNativeMenuColors(aScheme
));
271 aColor
= NS_RGB(0xe0, 0xe0, 0xe6);
273 case ColorID::MozMenuhoverdisabled
:
274 if (UseNonNativeMenuColors(aScheme
)) {
275 aColor
= NS_RGB(0xf0, 0xf0, 0xf3);
278 aColor
= NS_TRANSPARENT
;
280 case ColorID::Infobackground
:
283 case ColorID::Infotext
:
284 idx
= COLOR_INFOTEXT
;
287 if (UseNonNativeMenuColors(aScheme
)) {
288 aColor
= NS_RGB(0xf9, 0xf9, 0xfb);
293 case ColorID::Menutext
:
294 if (UseNonNativeMenuColors(aScheme
)) {
295 aColor
= kNonNativeMenuText
;
298 idx
= COLOR_MENUTEXT
;
300 case ColorID::Scrollbar
:
301 idx
= COLOR_SCROLLBAR
;
303 case ColorID::Threeddarkshadow
:
304 idx
= COLOR_3DDKSHADOW
;
306 case ColorID::Threedface
:
309 case ColorID::Threedhighlight
:
310 idx
= COLOR_3DHIGHLIGHT
;
312 case ColorID::Threedlightshadow
:
313 case ColorID::Buttonborder
:
314 case ColorID::MozDisabledfield
:
315 case ColorID::MozSidebarborder
:
318 case ColorID::Threedshadow
:
319 idx
= COLOR_3DSHADOW
;
321 case ColorID::Window
:
324 case ColorID::Windowframe
:
325 idx
= COLOR_WINDOWFRAME
;
327 case ColorID::Windowtext
:
328 idx
= COLOR_WINDOWTEXT
;
330 case ColorID::MozEventreerow
:
331 case ColorID::MozOddtreerow
:
333 case ColorID::MozSidebar
:
334 case ColorID::MozCombobox
:
337 case ColorID::Fieldtext
:
338 case ColorID::MozSidebartext
:
339 case ColorID::MozComboboxtext
:
340 idx
= COLOR_WINDOWTEXT
;
342 case ColorID::MozHeaderbar
:
343 case ColorID::MozHeaderbarinactive
:
344 case ColorID::MozDialog
:
347 case ColorID::Accentcolor
:
348 aColor
= mColorAccent
;
350 case ColorID::Accentcolortext
:
351 aColor
= mColorAccentText
;
353 case ColorID::MozHeaderbartext
:
354 case ColorID::MozHeaderbarinactivetext
:
355 case ColorID::MozDialogtext
:
356 case ColorID::MozColheadertext
:
357 case ColorID::MozColheaderhovertext
:
358 case ColorID::MozColheaderactivetext
:
359 idx
= COLOR_WINDOWTEXT
;
361 case ColorID::MozNativehyperlinktext
:
362 idx
= COLOR_HOTLIGHT
;
364 case ColorID::Marktext
:
366 case ColorID::SpellCheckerUnderline
:
367 aColor
= GetStandinForNativeColor(aID
, aScheme
);
371 res
= NS_ERROR_FAILURE
;
375 aColor
= GetColorForSysColorIndex(idx
);
380 nsresult
nsLookAndFeel::NativeGetInt(IntID aID
, int32_t& aResult
) {
382 nsresult res
= NS_OK
;
385 case IntID::ScrollButtonLeftMouseButtonAction
:
388 case IntID::ScrollButtonMiddleMouseButtonAction
:
389 case IntID::ScrollButtonRightMouseButtonAction
:
392 case IntID::CaretBlinkTime
:
393 aResult
= static_cast<int32_t>(::GetCaretBlinkTime());
395 case IntID::CaretBlinkCount
: {
396 int32_t timeout
= GetSystemParam(SPI_GETCARETTIMEOUT
, 5000);
397 auto blinkTime
= ::GetCaretBlinkTime();
398 if (timeout
<= 0 || blinkTime
<= 0) {
402 // 2 * blinkTime because this integer is a full blink cycle.
403 aResult
= std::ceil(float(timeout
) / (2.0f
* float(blinkTime
)));
406 case IntID::CaretWidth
:
409 case IntID::SelectTextfieldsOnKeyFocus
:
410 // Select textfield content when focused by kbd
411 // used by EventStateManager::sTextfieldSelectModel
414 case IntID::SubmenuDelay
:
415 // This will default to the Windows' default
417 aResult
= GetSystemParam(SPI_GETMENUSHOWDELAY
, 400);
419 case IntID::MenusCanOverlapOSBar
:
420 // we want XUL popups to be able to overlap the task bar.
423 case IntID::DragThresholdX
:
424 // The system metric is the number of pixels at which a drag should
425 // start. Our look and feel metric is the number of pixels you can
426 // move before starting a drag, so subtract 1.
427 aResult
= ::GetSystemMetrics(SM_CXDRAG
) - 1;
429 case IntID::DragThresholdY
:
430 aResult
= ::GetSystemMetrics(SM_CYDRAG
) - 1;
432 case IntID::UseAccessibilityTheme
:
433 // High contrast is a misnomer under Win32 -- any theme can be used with
434 // it, e.g. normal contrast with large fonts, low contrast, etc. The high
435 // contrast flag really means -- use this theme and don't override it.
436 aResult
= nsUXThemeData::IsHighContrastOn();
438 case IntID::ScrollArrowStyle
:
439 aResult
= eScrollArrowStyle_Single
;
441 case IntID::TreeOpenDelay
:
444 case IntID::TreeCloseDelay
:
447 case IntID::TreeLazyScrollDelay
:
450 case IntID::TreeScrollDelay
:
453 case IntID::TreeScrollLinesMax
:
456 case IntID::WindowsAccentColorInTitlebar
: {
457 aResult
= mTitlebarColors
.mUseAccent
;
459 case IntID::AlertNotificationOrigin
:
462 // Get task bar window handle
463 HWND shellWindow
= FindWindowW(L
"Shell_TrayWnd", nullptr);
465 if (shellWindow
!= nullptr) {
466 // Determine position
467 APPBARDATA appBarData
;
468 appBarData
.hWnd
= shellWindow
;
469 appBarData
.cbSize
= sizeof(appBarData
);
470 if (SHAppBarMessage(ABM_GETTASKBARPOS
, &appBarData
)) {
471 // Set alert origin as a bit field - see LookAndFeel.h
472 // 0 represents bottom right, sliding vertically.
473 switch (appBarData
.uEdge
) {
475 aResult
= NS_ALERT_HORIZONTAL
| NS_ALERT_LEFT
;
478 aResult
= NS_ALERT_HORIZONTAL
;
481 aResult
= NS_ALERT_TOP
;
484 // If the task bar is right-to-left,
485 // move the origin to the left
486 if (::GetWindowLong(shellWindow
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
)
487 aResult
|= NS_ALERT_LEFT
;
494 case IntID::IMERawInputUnderlineStyle
:
495 case IntID::IMEConvertedTextUnderlineStyle
:
496 aResult
= static_cast<int32_t>(StyleTextDecorationStyle::Dashed
);
498 case IntID::IMESelectedRawTextUnderlineStyle
:
499 case IntID::IMESelectedConvertedTextUnderline
:
500 aResult
= static_cast<int32_t>(StyleTextDecorationStyle::None
);
502 case IntID::SpellCheckerUnderlineStyle
:
503 aResult
= static_cast<int32_t>(StyleTextDecorationStyle::Wavy
);
505 case IntID::ScrollbarButtonAutoRepeatBehavior
:
508 case IntID::SwipeAnimationEnabled
:
509 // Forcibly enable the swipe animation on Windows. It doesn't matter on
510 // platforms where "Drag two fingers to scroll" isn't supported since on
511 // the platforms we will never generate any swipe gesture events.
514 case IntID::UseOverlayScrollbars
:
515 aResult
= WindowsUIUtils::ComputeOverlayScrollbars();
517 case IntID::AllowOverlayScrollbarsOverlap
:
520 case IntID::ScrollbarDisplayOnMouseMove
:
523 case IntID::ScrollbarFadeBeginDelay
:
526 case IntID::ScrollbarFadeDuration
:
529 case IntID::ContextMenuOffsetVertical
:
530 case IntID::ContextMenuOffsetHorizontal
:
533 case IntID::TooltipOffsetVertical
:
534 aResult
= GetTooltipOffsetVertical();
536 case IntID::SystemUsesDarkTheme
:
537 aResult
= SystemWantsDarkTheme();
539 case IntID::SystemScrollbarSize
:
540 aResult
= std::max(WinUtils::GetSystemMetricsForDpi(SM_CXVSCROLL
, 96),
541 WinUtils::GetSystemMetricsForDpi(SM_CXHSCROLL
, 96));
543 case IntID::PrefersReducedMotion
: {
545 ::SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION
, 0, &enable
, 0);
549 case IntID::PrefersReducedTransparency
: {
550 // Prefers reduced transparency if the option for "Transparency Effects"
552 aResult
= !WindowsUIUtils::ComputeTransparencyEffects();
555 case IntID::InvertedColors
: {
556 // Color filter values
558 // 2: Grayscale inverted
559 aResult
= mCurrentColorFilter
== 1 || mCurrentColorFilter
== 2;
562 case IntID::PrimaryPointerCapabilities
: {
563 aResult
= static_cast<int32_t>(
564 widget::WinUtils::GetPrimaryPointerCapabilities());
567 case IntID::AllPointerCapabilities
: {
569 static_cast<int32_t>(widget::WinUtils::GetAllPointerCapabilities());
572 case IntID::TouchDeviceSupportPresent
:
573 aResult
= !!WinUtils::IsTouchDeviceSupportPresent();
575 case IntID::PanelAnimations
:
578 case IntID::HideCursorWhileTyping
: {
580 ::SystemParametersInfoW(SPI_GETMOUSEVANISH
, 0, &enable
, 0);
586 res
= NS_ERROR_FAILURE
;
591 nsresult
nsLookAndFeel::NativeGetFloat(FloatID aID
, float& aResult
) {
592 nsresult res
= NS_OK
;
595 case FloatID::IMEUnderlineRelativeSize
:
598 case FloatID::SpellCheckerUnderlineRelativeSize
:
601 case FloatID::TextScaleFactor
:
602 aResult
= WindowsUIUtils::ComputeTextScaleFactor();
606 res
= NS_ERROR_FAILURE
;
611 LookAndFeelFont
nsLookAndFeel::GetLookAndFeelFontInternal(
612 const LOGFONTW
& aLogFont
, bool aUseShellDlg
) {
613 LookAndFeelFont result
{};
615 result
.haveFont() = false;
617 // Get scaling factor from physical to logical pixels
619 1.0 / WinUtils::SystemScaleFactor() / LookAndFeel::GetTextScaleFactor();
621 // The lfHeight is in pixels, and it needs to be adjusted for the
622 // device it will be displayed on.
623 // Screens and Printers will differ in DPI
625 // So this accounts for the difference in the DeviceContexts
626 // The pixelScale will typically be 1.0 for the screen
627 // (though larger for hi-dpi screens where the Windows resolution
628 // scale factor is 125% or 150% or even more), and could be
629 // any value when going to a printer, for example pixelScale is
630 // 6.25 when going to a 600dpi printer.
631 float pixelHeight
= -aLogFont
.lfHeight
;
632 if (pixelHeight
< 0) {
633 nsAutoFont
hFont(::CreateFontIndirectW(&aLogFont
));
638 nsAutoHDC
dc(::GetDC(nullptr));
639 HGDIOBJ hObject
= ::SelectObject(dc
, hFont
);
641 ::GetTextMetrics(dc
, &tm
);
642 ::SelectObject(dc
, hObject
);
644 pixelHeight
= tm
.tmAscent
;
647 pixelHeight
*= pixelScale
;
649 // we have problem on Simplified Chinese system because the system
650 // report the default font size is 8 points. but if we use 8, the text
651 // display very ugly. force it to be at 9 points (12 pixels) on that
652 // system (cp936), but leave other sizes alone.
653 if (pixelHeight
< 12 && ::GetACP() == 936) {
657 result
.haveFont() = true;
660 result
.name() = u
"MS Shell Dlg 2"_ns
;
662 result
.name() = aLogFont
.lfFaceName
;
665 result
.size() = pixelHeight
;
666 result
.italic() = !!aLogFont
.lfItalic
;
667 // FIXME: Other weights?
669 ((aLogFont
.lfWeight
== FW_BOLD
) ? FontWeight::BOLD
: FontWeight::NORMAL
)
675 LookAndFeelFont
nsLookAndFeel::GetLookAndFeelFont(LookAndFeel::FontID anID
) {
676 LookAndFeelFont result
{};
678 result
.haveFont() = false;
680 // FontID::Icon is handled differently than the others
681 if (anID
== LookAndFeel::FontID::Icon
) {
683 if (::SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, sizeof(logFont
),
684 (PVOID
)&logFont
, 0)) {
685 result
= GetLookAndFeelFontInternal(logFont
, false);
690 NONCLIENTMETRICSW ncm
;
691 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
692 if (!::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
),
698 case LookAndFeel::FontID::Menu
:
699 case LookAndFeel::FontID::MozPullDownMenu
:
700 result
= GetLookAndFeelFontInternal(ncm
.lfMenuFont
, false);
702 case LookAndFeel::FontID::Caption
:
703 result
= GetLookAndFeelFontInternal(ncm
.lfCaptionFont
, false);
705 case LookAndFeel::FontID::SmallCaption
:
706 result
= GetLookAndFeelFontInternal(ncm
.lfSmCaptionFont
, false);
708 case LookAndFeel::FontID::StatusBar
:
709 result
= GetLookAndFeelFontInternal(ncm
.lfStatusFont
, false);
711 case LookAndFeel::FontID::MozButton
:
712 case LookAndFeel::FontID::MozField
:
713 case LookAndFeel::FontID::MozList
:
714 // XXX It's not clear to me whether this is exactly the right
715 // set of LookAndFeel values to map to the dialog font; we may
716 // want to add or remove cases here after reviewing the visual
717 // results under various Windows versions.
718 result
= GetLookAndFeelFontInternal(ncm
.lfMessageFont
, true);
721 result
= GetLookAndFeelFontInternal(ncm
.lfMessageFont
, false);
728 bool nsLookAndFeel::NativeGetFont(LookAndFeel::FontID anID
, nsString
& aFontName
,
729 gfxFontStyle
& aFontStyle
) {
730 LookAndFeelFont font
= GetLookAndFeelFont(anID
);
731 return LookAndFeelFontToStyle(font
, aFontName
, aFontStyle
);
735 char16_t
nsLookAndFeel::GetPasswordCharacterImpl() {
736 #define UNICODE_BLACK_CIRCLE_CHAR 0x25cf
737 return UNICODE_BLACK_CIRCLE_CHAR
;
740 static nscolor
GetAccentColorText(const nscolor aAccentColor
) {
741 // We want the color that we return for text that will be drawn over
742 // a background that has the accent color to have good contrast with
743 // the accent color. Windows itself uses either white or black text
744 // depending on how light or dark the accent color is. We do the same
745 // here based on the luminance of the accent color with a threshhold
746 // value. This algorithm should match what Windows does. It comes from:
748 // https://docs.microsoft.com/en-us/windows/uwp/style/color
749 float luminance
= (NS_GET_R(aAccentColor
) * 2 + NS_GET_G(aAccentColor
) * 5 +
750 NS_GET_B(aAccentColor
)) /
752 return luminance
<= 128 ? NS_RGB(255, 255, 255) : NS_RGB(0, 0, 0);
755 static Maybe
<nscolor
> GetAccentColorText(const Maybe
<nscolor
>& aAccentColor
) {
759 return Some(GetAccentColorText(*aAccentColor
));
762 nscolor
nsLookAndFeel::GetColorForSysColorIndex(int index
) {
763 MOZ_ASSERT(index
>= SYS_COLOR_MIN
&& index
<= SYS_COLOR_MAX
);
764 return mSysColorTable
[index
- SYS_COLOR_MIN
];
767 auto nsLookAndFeel::ComputeTitlebarColors() -> TitlebarColors
{
768 TitlebarColors result
;
770 // Start with the native / non-accent-in-titlebar colors.
771 result
.mActiveLight
= {GetColorForSysColorIndex(COLOR_ACTIVECAPTION
),
772 GetColorForSysColorIndex(COLOR_CAPTIONTEXT
),
773 GetColorForSysColorIndex(COLOR_ACTIVEBORDER
)};
775 result
.mInactiveLight
= {GetColorForSysColorIndex(COLOR_INACTIVECAPTION
),
776 GetColorForSysColorIndex(COLOR_INACTIVECAPTIONTEXT
),
777 GetColorForSysColorIndex(COLOR_INACTIVEBORDER
)};
779 if (!nsUXThemeData::IsHighContrastOn()) {
780 // Use our non-native colors.
781 result
.mActiveLight
= {
782 GetStandinForNativeColor(ColorID::Activecaption
, ColorScheme::Light
),
783 GetStandinForNativeColor(ColorID::Captiontext
, ColorScheme::Light
),
784 GetStandinForNativeColor(ColorID::Activeborder
, ColorScheme::Light
)};
785 result
.mInactiveLight
= {
786 GetStandinForNativeColor(ColorID::Inactivecaption
, ColorScheme::Light
),
787 GetStandinForNativeColor(ColorID::Inactivecaptiontext
,
789 GetStandinForNativeColor(ColorID::Inactiveborder
, ColorScheme::Light
)};
792 // Our dark colors are always non-native.
793 result
.mActiveDark
= {*GenericDarkColor(ColorID::Activecaption
),
794 *GenericDarkColor(ColorID::Captiontext
),
795 *GenericDarkColor(ColorID::Activeborder
)};
796 result
.mInactiveDark
= {*GenericDarkColor(ColorID::Inactivecaption
),
797 *GenericDarkColor(ColorID::Inactivecaptiontext
),
798 *GenericDarkColor(ColorID::Inactiveborder
)};
800 // TODO(bug 1825241): Somehow get notified when this changes? Hopefully the
801 // sys color notification is enough.
802 WinRegistry::Key
dwmKey(HKEY_CURRENT_USER
,
803 u
"SOFTWARE\\Microsoft\\Windows\\DWM"_ns
,
804 WinRegistry::KeyMode::QueryValue
);
805 if (NS_WARN_IF(!dwmKey
)) {
809 // The order of the color components in the DWORD stored in the registry
810 // happens to be the same order as we store the components in nscolor
811 // so we can just assign directly here.
812 result
.mAccent
= dwmKey
.GetValueAsDword(u
"AccentColor"_ns
);
813 result
.mAccentText
= GetAccentColorText(result
.mAccent
);
815 if (!result
.mAccent
) {
819 result
.mAccentInactive
= dwmKey
.GetValueAsDword(u
"AccentColorInactive"_ns
);
820 result
.mAccentInactiveText
= GetAccentColorText(result
.mAccentInactive
);
822 // The ColorPrevalence value is set to 1 when the "Show color on title bar"
823 // setting in the Color section of Window's Personalization settings is
826 dwmKey
.GetValueAsDword(u
"ColorPrevalence"_ns
).valueOr(0) == 1;
827 if (!result
.mUseAccent
) {
831 // TODO(emilio): Consider reading ColorizationColorBalance to compute a
832 // more correct border color, see [1]. Though for opaque accent colors this
836 // https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/color/win/accent_color_observer.cc;l=42;drc=9d4eb7ed25296abba8fd525a6bdd0fdbf4bcdd9f
837 result
.mActiveDark
.mBorder
= result
.mActiveLight
.mBorder
= *result
.mAccent
;
838 result
.mInactiveDark
.mBorder
= result
.mInactiveLight
.mBorder
=
839 result
.mAccentInactive
.valueOr(NS_RGB(57, 57, 57));
840 result
.mActiveLight
.mBg
= result
.mActiveDark
.mBg
= *result
.mAccent
;
841 result
.mActiveLight
.mFg
= result
.mActiveDark
.mFg
= *result
.mAccentText
;
842 if (result
.mAccentInactive
) {
843 result
.mInactiveLight
.mBg
= result
.mInactiveDark
.mBg
=
844 *result
.mAccentInactive
;
845 result
.mInactiveLight
.mFg
= result
.mInactiveDark
.mFg
=
846 *result
.mAccentInactiveText
;
848 // This is hand-picked to .8 to change the accent color a bit but not too
850 constexpr uint8_t kBgAlpha
= 208;
851 const auto BlendWithAlpha
= [](nscolor aBg
, nscolor aFg
,
852 uint8_t aAlpha
) -> nscolor
{
853 return NS_ComposeColors(
854 aBg
, NS_RGBA(NS_GET_R(aFg
), NS_GET_G(aFg
), NS_GET_B(aFg
), aAlpha
));
856 result
.mInactiveLight
.mBg
=
857 BlendWithAlpha(NS_RGB(255, 255, 255), *result
.mAccent
, kBgAlpha
);
858 result
.mInactiveDark
.mBg
=
859 BlendWithAlpha(NS_RGB(0, 0, 0), *result
.mAccent
, kBgAlpha
);
860 result
.mInactiveLight
.mFg
= result
.mInactiveDark
.mFg
= *result
.mAccentText
;
865 void nsLookAndFeel::EnsureInit() {
871 mColorMenuHoverText
=
872 ::GetColorFromTheme(eUXMenu
, MENU_POPUPITEM
, MPI_HOT
, TMT_TEXTCOLOR
);
874 // Fill out the sys color table.
875 for (int i
= SYS_COLOR_MIN
; i
<= SYS_COLOR_MAX
; ++i
) {
876 mSysColorTable
[i
- SYS_COLOR_MIN
] = [&] {
877 if (auto c
= WindowsUIUtils::GetSystemColor(ColorScheme::Light
, i
)) {
880 DWORD color
= ::GetSysColor(i
);
881 return COLOREF_2_NSRGB(color
);
886 WindowsUIUtils::GetSystemColor(ColorScheme::Dark
, COLOR_HIGHLIGHT
);
888 WindowsUIUtils::GetSystemColor(ColorScheme::Dark
, COLOR_HIGHLIGHTTEXT
);
890 mTitlebarColors
= ComputeTitlebarColors();
893 if (auto accent
= WindowsUIUtils::GetAccentColor()) {
896 // Try the titlebar accent as a fallback.
897 if (mTitlebarColors
.mAccent
) {
898 return *mTitlebarColors
.mAccent
;
900 // Seems to be the default color (hardcoded because of bug 1065998)
901 return NS_RGB(0, 120, 215);
903 mColorAccentText
= GetAccentColorText(mColorAccent
);
905 if (!mColorFilterWatcher
) {
906 WinRegistry::Key
key(
907 HKEY_CURRENT_USER
, u
"Software\\Microsoft\\ColorFiltering"_ns
,
908 WinRegistry::KeyMode::QueryValue
| WinRegistry::KeyMode::Notify
);
910 mColorFilterWatcher
= MakeUnique
<WinRegistry::KeyWatcher
>(
911 std::move(key
), GetCurrentSerialEventTarget(), [this] {
912 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
913 if (mCurrentColorFilter
!= SystemColorFilter()) {
914 LookAndFeel::NotifyChangedAllWindows(
915 widget::ThemeChangeKind::MediaQueriesOnly
);
920 mCurrentColorFilter
= SystemColorFilter();