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::MozButtonhoverface
:
107 case ColorID::MozButtonactivetext
:
108 return nsUXThemeData::IsHighContrastOn();
109 case ColorID::MozMenuhover
:
110 return !UseNonNativeMenuColors(aScheme
);
111 case ColorID::Highlight
:
112 case ColorID::Selecteditem
:
113 // We prefer the generic dark selection color if we don't have an
115 return aScheme
!= ColorScheme::Dark
|| mDarkHighlight
;
116 case ColorID::IMESelectedRawTextBackground
:
117 case ColorID::IMESelectedConvertedTextBackground
:
124 auto IsHighlightTextColor
= [&] {
126 case ColorID::MozButtonhovertext
:
127 case ColorID::MozButtonactiveface
:
128 return nsUXThemeData::IsHighContrastOn();
129 case ColorID::MozMenubarhovertext
:
130 if (UseNonNativeMenuColors(aScheme
)) {
134 case ColorID::MozMenuhovertext
:
135 if (UseNonNativeMenuColors(aScheme
)) {
138 return !mColorMenuHoverText
;
139 case ColorID::Highlighttext
:
140 case ColorID::Selecteditemtext
:
141 // We prefer the generic dark selection color if we don't have an
143 return aScheme
!= ColorScheme::Dark
|| mDarkHighlightText
;
144 case ColorID::IMESelectedRawTextForeground
:
145 case ColorID::IMESelectedConvertedTextForeground
:
152 if (IsHighlightColor()) {
153 if (aScheme
== ColorScheme::Dark
&& mDarkHighlight
) {
154 aColor
= *mDarkHighlight
;
156 aColor
= GetColorForSysColorIndex(COLOR_HIGHLIGHT
);
161 if (IsHighlightTextColor()) {
162 if (aScheme
== ColorScheme::Dark
&& mDarkHighlightText
) {
163 aColor
= *mDarkHighlightText
;
165 aColor
= GetColorForSysColorIndex(COLOR_HIGHLIGHTTEXT
);
170 // Titlebar colors are color-scheme aware.
172 case ColorID::Activecaption
:
173 aColor
= mTitlebarColors
.Get(aScheme
, true).mBg
;
175 case ColorID::Captiontext
:
176 aColor
= mTitlebarColors
.Get(aScheme
, true).mFg
;
178 case ColorID::Activeborder
:
179 aColor
= mTitlebarColors
.Get(aScheme
, true).mBorder
;
181 case ColorID::Inactivecaption
:
182 aColor
= mTitlebarColors
.Get(aScheme
, false).mBg
;
184 case ColorID::Inactivecaptiontext
:
185 aColor
= mTitlebarColors
.Get(aScheme
, false).mFg
;
187 case ColorID::Inactiveborder
:
188 aColor
= mTitlebarColors
.Get(aScheme
, false).mBorder
;
194 if (aScheme
== ColorScheme::Dark
) {
195 if (auto color
= GenericDarkColor(aID
)) {
201 static constexpr auto kNonNativeMenuText
= NS_RGB(0x15, 0x14, 0x1a);
202 nsresult res
= NS_OK
;
205 case ColorID::IMERawInputBackground
:
206 case ColorID::IMEConvertedTextBackground
:
207 aColor
= NS_TRANSPARENT
;
209 case ColorID::IMERawInputForeground
:
210 case ColorID::IMEConvertedTextForeground
:
211 aColor
= NS_SAME_AS_FOREGROUND_COLOR
;
213 case ColorID::IMERawInputUnderline
:
214 case ColorID::IMEConvertedTextUnderline
:
215 aColor
= NS_SAME_AS_FOREGROUND_COLOR
;
217 case ColorID::IMESelectedRawTextUnderline
:
218 case ColorID::IMESelectedConvertedTextUnderline
:
219 aColor
= NS_TRANSPARENT
;
222 // New CSS 2 Color definitions
223 case ColorID::Appworkspace
:
224 idx
= COLOR_APPWORKSPACE
;
226 case ColorID::Background
:
227 idx
= COLOR_BACKGROUND
;
229 case ColorID::Buttonface
:
230 case ColorID::MozButtonhoverface
:
231 case ColorID::MozButtonactiveface
:
232 case ColorID::MozButtondisabledface
:
233 case ColorID::MozColheader
:
234 case ColorID::MozColheaderhover
:
235 case ColorID::MozColheaderactive
:
238 case ColorID::Buttonhighlight
:
239 idx
= COLOR_BTNHIGHLIGHT
;
241 case ColorID::Buttonshadow
:
242 idx
= COLOR_BTNSHADOW
;
244 case ColorID::Buttontext
:
245 case ColorID::MozButtonhovertext
:
246 case ColorID::MozButtonactivetext
:
249 case ColorID::MozCellhighlighttext
:
250 aColor
= NS_RGB(0, 0, 0);
252 case ColorID::MozCellhighlight
:
253 aColor
= NS_RGB(206, 206, 206);
255 case ColorID::Graytext
:
256 idx
= COLOR_GRAYTEXT
;
258 case ColorID::MozMenubarhovertext
:
259 if (UseNonNativeMenuColors(aScheme
)) {
260 aColor
= kNonNativeMenuText
;
264 case ColorID::MozMenuhovertext
:
265 if (UseNonNativeMenuColors(aScheme
)) {
266 aColor
= kNonNativeMenuText
;
269 if (mColorMenuHoverText
) {
270 aColor
= *mColorMenuHoverText
;
273 idx
= COLOR_HIGHLIGHTTEXT
;
275 case ColorID::MozMenuhover
:
276 MOZ_ASSERT(UseNonNativeMenuColors(aScheme
));
277 aColor
= NS_RGB(0xe0, 0xe0, 0xe6);
279 case ColorID::MozMenuhoverdisabled
:
280 if (UseNonNativeMenuColors(aScheme
)) {
281 aColor
= NS_RGB(0xf0, 0xf0, 0xf3);
284 aColor
= NS_TRANSPARENT
;
286 case ColorID::Infobackground
:
289 case ColorID::Infotext
:
290 idx
= COLOR_INFOTEXT
;
293 if (UseNonNativeMenuColors(aScheme
)) {
294 aColor
= NS_RGB(0xf9, 0xf9, 0xfb);
299 case ColorID::Menutext
:
300 if (UseNonNativeMenuColors(aScheme
)) {
301 aColor
= kNonNativeMenuText
;
304 idx
= COLOR_MENUTEXT
;
306 case ColorID::Scrollbar
:
307 idx
= COLOR_SCROLLBAR
;
309 case ColorID::Threeddarkshadow
:
310 idx
= COLOR_3DDKSHADOW
;
312 case ColorID::Threedface
:
315 case ColorID::Threedhighlight
:
316 idx
= COLOR_3DHIGHLIGHT
;
318 case ColorID::Threedlightshadow
:
319 case ColorID::Buttonborder
:
320 case ColorID::MozSidebarborder
:
323 case ColorID::Threedshadow
:
324 idx
= COLOR_3DSHADOW
;
326 case ColorID::Window
:
329 case ColorID::Windowframe
:
330 idx
= COLOR_WINDOWFRAME
;
332 case ColorID::Windowtext
:
333 idx
= COLOR_WINDOWTEXT
;
335 case ColorID::MozDisabledfield
:
336 idx
= nsUXThemeData::IsHighContrastOn() ? COLOR_BTNFACE
: COLOR_3DLIGHT
;
339 idx
= nsUXThemeData::IsHighContrastOn() ? COLOR_BTNFACE
: COLOR_WINDOW
;
341 case ColorID::Fieldtext
:
343 nsUXThemeData::IsHighContrastOn() ? COLOR_BTNTEXT
: COLOR_WINDOWTEXT
;
345 case ColorID::MozEventreerow
:
346 case ColorID::MozOddtreerow
:
347 case ColorID::MozSidebar
:
348 case ColorID::MozCombobox
:
351 case ColorID::MozSidebartext
:
352 case ColorID::MozComboboxtext
:
353 idx
= COLOR_WINDOWTEXT
;
355 case ColorID::MozHeaderbar
:
356 case ColorID::MozHeaderbarinactive
:
357 case ColorID::MozDialog
:
360 case ColorID::Accentcolor
:
361 aColor
= mColorAccent
;
363 case ColorID::Accentcolortext
:
364 aColor
= mColorAccentText
;
366 case ColorID::MozHeaderbartext
:
367 case ColorID::MozHeaderbarinactivetext
:
368 case ColorID::MozDialogtext
:
369 case ColorID::MozColheadertext
:
370 case ColorID::MozColheaderhovertext
:
371 case ColorID::MozColheaderactivetext
:
372 idx
= COLOR_WINDOWTEXT
;
374 case ColorID::MozNativehyperlinktext
:
375 idx
= COLOR_HOTLIGHT
;
377 case ColorID::Marktext
:
379 case ColorID::SpellCheckerUnderline
:
380 case ColorID::MozAutofillBackground
:
381 case ColorID::TargetTextBackground
:
382 case ColorID::TargetTextForeground
:
383 aColor
= GetStandinForNativeColor(aID
, aScheme
);
387 res
= NS_ERROR_FAILURE
;
391 aColor
= GetColorForSysColorIndex(idx
);
396 nsresult
nsLookAndFeel::NativeGetInt(IntID aID
, int32_t& aResult
) {
398 nsresult res
= NS_OK
;
401 case IntID::ScrollButtonLeftMouseButtonAction
:
404 case IntID::ScrollButtonMiddleMouseButtonAction
:
405 case IntID::ScrollButtonRightMouseButtonAction
:
408 case IntID::CaretBlinkTime
:
409 aResult
= static_cast<int32_t>(::GetCaretBlinkTime());
411 case IntID::CaretBlinkCount
: {
412 int32_t timeout
= GetSystemParam(SPI_GETCARETTIMEOUT
, 5000);
413 auto blinkTime
= ::GetCaretBlinkTime();
414 if (timeout
<= 0 || blinkTime
<= 0) {
418 // 2 * blinkTime because this integer is a full blink cycle.
419 aResult
= std::ceil(float(timeout
) / (2.0f
* float(blinkTime
)));
422 case IntID::CaretWidth
:
425 case IntID::SelectTextfieldsOnKeyFocus
:
426 // Select textfield content when focused by kbd
427 // used by EventStateManager::sTextfieldSelectModel
430 case IntID::SubmenuDelay
:
431 // This will default to the Windows' default
433 aResult
= GetSystemParam(SPI_GETMENUSHOWDELAY
, 400);
435 case IntID::MenusCanOverlapOSBar
:
436 // we want XUL popups to be able to overlap the task bar.
439 case IntID::DragThresholdX
:
440 // The system metric is the number of pixels at which a drag should
441 // start. Our look and feel metric is the number of pixels you can
442 // move before starting a drag, so subtract 1.
443 aResult
= ::GetSystemMetrics(SM_CXDRAG
) - 1;
445 case IntID::DragThresholdY
:
446 aResult
= ::GetSystemMetrics(SM_CYDRAG
) - 1;
448 case IntID::UseAccessibilityTheme
:
449 // High contrast is a misnomer under Win32 -- any theme can be used with
450 // it, e.g. normal contrast with large fonts, low contrast, etc. The high
451 // contrast flag really means -- use this theme and don't override it.
452 aResult
= nsUXThemeData::IsHighContrastOn();
454 case IntID::ScrollArrowStyle
:
455 aResult
= eScrollArrowStyle_Single
;
457 case IntID::TreeOpenDelay
:
460 case IntID::TreeCloseDelay
:
463 case IntID::TreeLazyScrollDelay
:
466 case IntID::TreeScrollDelay
:
469 case IntID::TreeScrollLinesMax
:
472 case IntID::WindowsAccentColorInTitlebar
:
473 aResult
= mTitlebarColors
.mUseAccent
;
475 case IntID::WindowsMica
:
476 aResult
= WinUtils::MicaEnabled();
478 case IntID::AlertNotificationOrigin
:
481 // Get task bar window handle
482 HWND shellWindow
= FindWindowW(L
"Shell_TrayWnd", nullptr);
484 if (shellWindow
!= nullptr) {
485 // Determine position
486 APPBARDATA appBarData
;
487 appBarData
.hWnd
= shellWindow
;
488 appBarData
.cbSize
= sizeof(appBarData
);
489 if (SHAppBarMessage(ABM_GETTASKBARPOS
, &appBarData
)) {
490 // Set alert origin as a bit field - see LookAndFeel.h
491 // 0 represents bottom right, sliding vertically.
492 switch (appBarData
.uEdge
) {
494 aResult
= NS_ALERT_HORIZONTAL
| NS_ALERT_LEFT
;
497 aResult
= NS_ALERT_HORIZONTAL
;
500 aResult
= NS_ALERT_TOP
;
503 // If the task bar is right-to-left,
504 // move the origin to the left
505 if (::GetWindowLong(shellWindow
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
)
506 aResult
|= NS_ALERT_LEFT
;
513 case IntID::IMERawInputUnderlineStyle
:
514 case IntID::IMEConvertedTextUnderlineStyle
:
515 aResult
= static_cast<int32_t>(StyleTextDecorationStyle::Dashed
);
517 case IntID::IMESelectedRawTextUnderlineStyle
:
518 case IntID::IMESelectedConvertedTextUnderline
:
519 aResult
= static_cast<int32_t>(StyleTextDecorationStyle::None
);
521 case IntID::SpellCheckerUnderlineStyle
:
522 aResult
= static_cast<int32_t>(StyleTextDecorationStyle::Wavy
);
524 case IntID::ScrollbarButtonAutoRepeatBehavior
:
527 case IntID::SwipeAnimationEnabled
:
528 // Forcibly enable the swipe animation on Windows. It doesn't matter on
529 // platforms where "Drag two fingers to scroll" isn't supported since on
530 // the platforms we will never generate any swipe gesture events.
533 case IntID::UseOverlayScrollbars
:
534 aResult
= WindowsUIUtils::ComputeOverlayScrollbars();
536 case IntID::AllowOverlayScrollbarsOverlap
:
539 case IntID::ScrollbarDisplayOnMouseMove
:
542 case IntID::ScrollbarFadeBeginDelay
:
545 case IntID::ScrollbarFadeDuration
:
548 case IntID::ContextMenuOffsetVertical
:
549 case IntID::ContextMenuOffsetHorizontal
:
552 case IntID::TooltipOffsetVertical
:
553 aResult
= GetTooltipOffsetVertical();
555 case IntID::SystemUsesDarkTheme
:
556 aResult
= SystemWantsDarkTheme();
558 case IntID::SystemScrollbarSize
:
559 aResult
= std::max(WinUtils::GetSystemMetricsForDpi(SM_CXVSCROLL
, 96),
560 WinUtils::GetSystemMetricsForDpi(SM_CXHSCROLL
, 96));
562 case IntID::PrefersReducedMotion
: {
564 ::SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION
, 0, &enable
, 0);
568 case IntID::PrefersReducedTransparency
: {
569 // Prefers reduced transparency if the option for "Transparency Effects"
571 aResult
= !WindowsUIUtils::ComputeTransparencyEffects();
574 case IntID::InvertedColors
: {
575 // Color filter values
577 // 2: Grayscale inverted
578 aResult
= mCurrentColorFilter
== 1 || mCurrentColorFilter
== 2;
581 case IntID::PrimaryPointerCapabilities
: {
582 aResult
= static_cast<int32_t>(
583 widget::WinUtils::GetPrimaryPointerCapabilities());
586 case IntID::AllPointerCapabilities
: {
588 static_cast<int32_t>(widget::WinUtils::GetAllPointerCapabilities());
591 case IntID::TouchDeviceSupportPresent
:
592 aResult
= !!WinUtils::IsTouchDeviceSupportPresent();
594 case IntID::PanelAnimations
:
597 case IntID::HideCursorWhileTyping
: {
599 ::SystemParametersInfoW(SPI_GETMOUSEVANISH
, 0, &enable
, 0);
603 case IntID::PointingDeviceKinds
: {
604 LookAndFeel::PointingDeviceKinds result
=
605 LookAndFeel::PointingDeviceKinds::None
;
606 if (WinUtils::SystemHasMouse()) {
607 result
|= LookAndFeel::PointingDeviceKinds::Mouse
;
609 if (WinUtils::SystemHasTouch()) {
610 result
|= LookAndFeel::PointingDeviceKinds::Touch
;
612 if (WinUtils::SystemHasPen()) {
613 result
|= LookAndFeel::PointingDeviceKinds::Pen
;
615 aResult
= static_cast<int32_t>(result
);
620 res
= NS_ERROR_FAILURE
;
625 nsresult
nsLookAndFeel::NativeGetFloat(FloatID aID
, float& aResult
) {
626 nsresult res
= NS_OK
;
629 case FloatID::IMEUnderlineRelativeSize
:
632 case FloatID::SpellCheckerUnderlineRelativeSize
:
635 case FloatID::TextScaleFactor
:
636 aResult
= WindowsUIUtils::ComputeTextScaleFactor();
640 res
= NS_ERROR_FAILURE
;
645 LookAndFeelFont
nsLookAndFeel::GetLookAndFeelFontInternal(
646 const LOGFONTW
& aLogFont
, bool aUseShellDlg
) {
647 LookAndFeelFont result
{};
649 result
.haveFont() = false;
651 // Get scaling factor from physical to logical pixels
653 1.0 / WinUtils::SystemScaleFactor() / LookAndFeel::GetTextScaleFactor();
655 // The lfHeight is in pixels, and it needs to be adjusted for the
656 // device it will be displayed on.
657 // Screens and Printers will differ in DPI
659 // So this accounts for the difference in the DeviceContexts
660 // The pixelScale will typically be 1.0 for the screen
661 // (though larger for hi-dpi screens where the Windows resolution
662 // scale factor is 125% or 150% or even more), and could be
663 // any value when going to a printer, for example pixelScale is
664 // 6.25 when going to a 600dpi printer.
665 float pixelHeight
= -aLogFont
.lfHeight
;
666 if (pixelHeight
< 0) {
667 nsAutoFont
hFont(::CreateFontIndirectW(&aLogFont
));
672 nsAutoHDC
dc(::GetDC(nullptr));
673 HGDIOBJ hObject
= ::SelectObject(dc
, hFont
);
675 ::GetTextMetrics(dc
, &tm
);
676 ::SelectObject(dc
, hObject
);
678 pixelHeight
= tm
.tmAscent
;
681 pixelHeight
*= pixelScale
;
683 // we have problem on Simplified Chinese system because the system
684 // report the default font size is 8 points. but if we use 8, the text
685 // display very ugly. force it to be at 9 points (12 pixels) on that
686 // system (cp936), but leave other sizes alone.
687 if (pixelHeight
< 12 && ::GetACP() == 936) {
691 result
.haveFont() = true;
694 result
.name() = u
"MS Shell Dlg 2"_ns
;
696 result
.name() = aLogFont
.lfFaceName
;
699 result
.size() = pixelHeight
;
700 result
.italic() = !!aLogFont
.lfItalic
;
701 // FIXME: Other weights?
703 ((aLogFont
.lfWeight
== FW_BOLD
) ? FontWeight::BOLD
: FontWeight::NORMAL
)
709 LookAndFeelFont
nsLookAndFeel::GetLookAndFeelFont(LookAndFeel::FontID anID
) {
710 LookAndFeelFont result
{};
712 result
.haveFont() = false;
714 // FontID::Icon is handled differently than the others
715 if (anID
== LookAndFeel::FontID::Icon
) {
717 if (::SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, sizeof(logFont
),
718 (PVOID
)&logFont
, 0)) {
719 result
= GetLookAndFeelFontInternal(logFont
, false);
724 NONCLIENTMETRICSW ncm
;
725 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
726 if (!::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
),
732 case LookAndFeel::FontID::Menu
:
733 case LookAndFeel::FontID::MozPullDownMenu
:
734 result
= GetLookAndFeelFontInternal(ncm
.lfMenuFont
, false);
736 case LookAndFeel::FontID::Caption
:
737 result
= GetLookAndFeelFontInternal(ncm
.lfCaptionFont
, false);
739 case LookAndFeel::FontID::SmallCaption
:
740 result
= GetLookAndFeelFontInternal(ncm
.lfSmCaptionFont
, false);
742 case LookAndFeel::FontID::StatusBar
:
743 result
= GetLookAndFeelFontInternal(ncm
.lfStatusFont
, false);
745 case LookAndFeel::FontID::MozButton
:
746 case LookAndFeel::FontID::MozField
:
747 case LookAndFeel::FontID::MozList
:
748 // XXX It's not clear to me whether this is exactly the right
749 // set of LookAndFeel values to map to the dialog font; we may
750 // want to add or remove cases here after reviewing the visual
751 // results under various Windows versions.
752 result
= GetLookAndFeelFontInternal(ncm
.lfMessageFont
, true);
755 result
= GetLookAndFeelFontInternal(ncm
.lfMessageFont
, false);
762 bool nsLookAndFeel::NativeGetFont(LookAndFeel::FontID anID
, nsString
& aFontName
,
763 gfxFontStyle
& aFontStyle
) {
764 LookAndFeelFont font
= GetLookAndFeelFont(anID
);
765 return LookAndFeelFontToStyle(font
, aFontName
, aFontStyle
);
769 char16_t
nsLookAndFeel::GetPasswordCharacterImpl() {
770 #define UNICODE_BLACK_CIRCLE_CHAR 0x25cf
771 return UNICODE_BLACK_CIRCLE_CHAR
;
774 static nscolor
GetAccentColorText(const nscolor aAccentColor
) {
775 // We want the color that we return for text that will be drawn over
776 // a background that has the accent color to have good contrast with
777 // the accent color. Windows itself uses either white or black text
778 // depending on how light or dark the accent color is. We do the same
779 // here based on the luminance of the accent color with a threshhold
780 // value. This algorithm should match what Windows does. It comes from:
782 // https://docs.microsoft.com/en-us/windows/uwp/style/color
783 float luminance
= (NS_GET_R(aAccentColor
) * 2 + NS_GET_G(aAccentColor
) * 5 +
784 NS_GET_B(aAccentColor
)) /
786 return luminance
<= 128 ? NS_RGB(255, 255, 255) : NS_RGB(0, 0, 0);
789 static Maybe
<nscolor
> GetAccentColorText(const Maybe
<nscolor
>& aAccentColor
) {
793 return Some(GetAccentColorText(*aAccentColor
));
796 nscolor
nsLookAndFeel::GetColorForSysColorIndex(int index
) {
797 MOZ_ASSERT(index
>= SYS_COLOR_MIN
&& index
<= SYS_COLOR_MAX
);
798 return mSysColorTable
[index
- SYS_COLOR_MIN
];
801 auto nsLookAndFeel::ComputeTitlebarColors() -> TitlebarColors
{
802 TitlebarColors result
;
804 // Start with the native / non-accent-in-titlebar colors.
805 result
.mActiveLight
= {GetColorForSysColorIndex(COLOR_ACTIVECAPTION
),
806 GetColorForSysColorIndex(COLOR_CAPTIONTEXT
),
807 GetColorForSysColorIndex(COLOR_ACTIVEBORDER
)};
809 result
.mInactiveLight
= {GetColorForSysColorIndex(COLOR_INACTIVECAPTION
),
810 GetColorForSysColorIndex(COLOR_INACTIVECAPTIONTEXT
),
811 GetColorForSysColorIndex(COLOR_INACTIVEBORDER
)};
813 if (!nsUXThemeData::IsHighContrastOn()) {
814 // Use our non-native colors.
815 result
.mActiveLight
= {
816 GetStandinForNativeColor(ColorID::Activecaption
, ColorScheme::Light
),
817 GetStandinForNativeColor(ColorID::Captiontext
, ColorScheme::Light
),
818 GetStandinForNativeColor(ColorID::Activeborder
, ColorScheme::Light
)};
819 result
.mInactiveLight
= {
820 GetStandinForNativeColor(ColorID::Inactivecaption
, ColorScheme::Light
),
821 GetStandinForNativeColor(ColorID::Inactivecaptiontext
,
823 GetStandinForNativeColor(ColorID::Inactiveborder
, ColorScheme::Light
)};
826 // Our dark colors are always non-native.
827 result
.mActiveDark
= {*GenericDarkColor(ColorID::Activecaption
),
828 *GenericDarkColor(ColorID::Captiontext
),
829 *GenericDarkColor(ColorID::Activeborder
)};
830 result
.mInactiveDark
= {*GenericDarkColor(ColorID::Inactivecaption
),
831 *GenericDarkColor(ColorID::Inactivecaptiontext
),
832 *GenericDarkColor(ColorID::Inactiveborder
)};
834 // TODO(bug 1825241): Somehow get notified when this changes? Hopefully the
835 // sys color notification is enough.
836 WinRegistry::Key
dwmKey(HKEY_CURRENT_USER
,
837 u
"SOFTWARE\\Microsoft\\Windows\\DWM"_ns
,
838 WinRegistry::KeyMode::QueryValue
);
839 if (NS_WARN_IF(!dwmKey
)) {
843 // The order of the color components in the DWORD stored in the registry
844 // happens to be the same order as we store the components in nscolor
845 // so we can just assign directly here.
846 result
.mAccent
= dwmKey
.GetValueAsDword(u
"AccentColor"_ns
);
847 result
.mAccentText
= GetAccentColorText(result
.mAccent
);
849 if (!result
.mAccent
) {
853 result
.mAccentInactive
= dwmKey
.GetValueAsDword(u
"AccentColorInactive"_ns
);
854 result
.mAccentInactiveText
= GetAccentColorText(result
.mAccentInactive
);
856 if (WinUtils::MicaEnabled()) {
857 // Use transparent titlebar backgrounds when using mica.
858 result
.mActiveDark
.mBg
= result
.mActiveLight
.mBg
=
859 result
.mInactiveDark
.mBg
= result
.mInactiveLight
.mBg
= NS_TRANSPARENT
;
862 // The ColorPrevalence value is set to 1 when the "Show color on title bar"
863 // setting in the Color section of Window's Personalization settings is
866 dwmKey
.GetValueAsDword(u
"ColorPrevalence"_ns
).valueOr(0) == 1;
867 if (!result
.mUseAccent
) {
871 // TODO(emilio): Consider reading ColorizationColorBalance to compute a
872 // more correct border color, see [1]. Though for opaque accent colors this
876 // https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/color/win/accent_color_observer.cc;l=42;drc=9d4eb7ed25296abba8fd525a6bdd0fdbf4bcdd9f
877 result
.mActiveDark
.mBorder
= result
.mActiveLight
.mBorder
= *result
.mAccent
;
878 result
.mInactiveDark
.mBorder
= result
.mInactiveLight
.mBorder
=
879 result
.mAccentInactive
.valueOr(NS_RGB(57, 57, 57));
880 result
.mActiveLight
.mBg
= result
.mActiveDark
.mBg
= *result
.mAccent
;
881 result
.mActiveLight
.mFg
= result
.mActiveDark
.mFg
= *result
.mAccentText
;
882 if (result
.mAccentInactive
) {
883 result
.mInactiveLight
.mBg
= result
.mInactiveDark
.mBg
=
884 *result
.mAccentInactive
;
885 result
.mInactiveLight
.mFg
= result
.mInactiveDark
.mFg
=
886 *result
.mAccentInactiveText
;
888 // This is hand-picked to .8 to change the accent color a bit but not too
890 constexpr uint8_t kBgAlpha
= 208;
891 const auto BlendWithAlpha
= [](nscolor aBg
, nscolor aFg
,
892 uint8_t aAlpha
) -> nscolor
{
893 return NS_ComposeColors(
894 aBg
, NS_RGBA(NS_GET_R(aFg
), NS_GET_G(aFg
), NS_GET_B(aFg
), aAlpha
));
896 result
.mInactiveLight
.mBg
=
897 BlendWithAlpha(NS_RGB(255, 255, 255), *result
.mAccent
, kBgAlpha
);
898 result
.mInactiveDark
.mBg
=
899 BlendWithAlpha(NS_RGB(0, 0, 0), *result
.mAccent
, kBgAlpha
);
900 result
.mInactiveLight
.mFg
= result
.mInactiveDark
.mFg
= *result
.mAccentText
;
905 nsresult
nsLookAndFeel::GetKeyboardLayoutImpl(nsACString
& aLayout
) {
906 char layout
[KL_NAMELENGTH
];
907 if (!::GetKeyboardLayoutNameA(layout
)) {
908 return NS_ERROR_NOT_AVAILABLE
;
910 aLayout
.Assign(layout
);
915 void nsLookAndFeel::EnsureInit() {
921 mColorMenuHoverText
=
922 ::GetColorFromTheme(eUXMenu
, MENU_POPUPITEM
, MPI_HOT
, TMT_TEXTCOLOR
);
924 // Fill out the sys color table.
925 for (int i
= SYS_COLOR_MIN
; i
<= SYS_COLOR_MAX
; ++i
) {
926 mSysColorTable
[i
- SYS_COLOR_MIN
] = [&] {
927 if (auto c
= WindowsUIUtils::GetSystemColor(ColorScheme::Light
, i
)) {
930 DWORD color
= ::GetSysColor(i
);
931 return COLOREF_2_NSRGB(color
);
936 WindowsUIUtils::GetSystemColor(ColorScheme::Dark
, COLOR_HIGHLIGHT
);
938 WindowsUIUtils::GetSystemColor(ColorScheme::Dark
, COLOR_HIGHLIGHTTEXT
);
940 mTitlebarColors
= ComputeTitlebarColors();
943 if (auto accent
= WindowsUIUtils::GetAccentColor()) {
946 // Try the titlebar accent as a fallback.
947 if (mTitlebarColors
.mAccent
) {
948 return *mTitlebarColors
.mAccent
;
950 // Seems to be the default color (hardcoded because of bug 1065998)
951 return NS_RGB(0, 120, 215);
953 mColorAccentText
= GetAccentColorText(mColorAccent
);
955 if (!mColorFilterWatcher
) {
956 WinRegistry::Key
key(
957 HKEY_CURRENT_USER
, u
"Software\\Microsoft\\ColorFiltering"_ns
,
958 WinRegistry::KeyMode::QueryValue
| WinRegistry::KeyMode::Notify
);
960 mColorFilterWatcher
= MakeUnique
<WinRegistry::KeyWatcher
>(
961 std::move(key
), GetCurrentSerialEventTarget(), [this] {
962 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
963 if (mCurrentColorFilter
!= SystemColorFilter()) {
964 LookAndFeel::NotifyChangedAllWindows(
965 widget::ThemeChangeKind::MediaQueriesOnly
);
970 mCurrentColorFilter
= SystemColorFilter();