Skip to content

Commit 42e7afc

Browse files
anuagragithAnukrati Agrawal
andauthored
Fix inactive Modal title bar contrast (#16434)
* Change files * Fix inactive Modal title bar contrast Use theme-aware AppWindowTitleBar colors for active and inactive Modal caption states while preserving true window activation and runtime theme updates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8f4dac7d-4a14-4053-bd3b-a7cda2967006 --------- Co-authored-by: Anukrati Agrawal <anuagra@microsoft.com> Copilot-Session: 8f4dac7d-4a14-4053-bd3b-a7cda2967006
1 parent 6caef12 commit 42e7afc

2 files changed

Lines changed: 114 additions & 0 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "Fix Modal title bar contrast when the native window is inactive",
4+
"packageName": "react-native-windows",
5+
"email": "anuagra@microsoft.com",
6+
"dependentChangeType": "patch"
7+
}

vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,22 @@
1515

1616
namespace winrt::Microsoft::ReactNative::Composition::implementation {
1717

18+
static winrt::Windows::UI::Color CompositeOverOpaqueBackground(
19+
const winrt::Windows::UI::Color &foreground,
20+
const winrt::Windows::UI::Color &background) noexcept {
21+
const uint32_t alpha = foreground.A;
22+
const uint32_t inverseAlpha = 0xFF - alpha;
23+
const auto compositeChannel = [alpha, inverseAlpha](uint8_t foregroundChannel, uint8_t backgroundChannel) {
24+
return static_cast<uint8_t>((foregroundChannel * alpha + backgroundChannel * inverseAlpha + 0x7F) / 0xFF);
25+
};
26+
27+
return {
28+
0xFF,
29+
compositeChannel(foreground.R, background.R),
30+
compositeChannel(foreground.G, background.G),
31+
compositeChannel(foreground.B, background.B)};
32+
}
33+
1834
struct ModalHostState
1935
: winrt::implements<ModalHostState, winrt::Microsoft::ReactNative::Composition::IPortalStateData> {
2036
ModalHostState(winrt::Microsoft::ReactNative::LayoutConstraints layoutConstraints, float scaleFactor)
@@ -36,6 +52,8 @@ struct ModalHostState
3652
struct ModalHostView : public winrt::implements<ModalHostView, winrt::Windows::Foundation::IInspectable>,
3753
::Microsoft::ReactNativeSpecs::BaseModalHostView<ModalHostView> {
3854
~ModalHostView() {
55+
UnsubscribeFromThemeChanges();
56+
3957
if (m_popUp) {
4058
// Unregister closing event handler
4159
if (m_appWindowClosingToken) {
@@ -171,16 +189,55 @@ struct ModalHostView : public winrt::implements<ModalHostView, winrt::Windows::F
171189

172190
private:
173191
void OnMounted(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
192+
SubscribeToThemeChanges(view);
174193
m_mounted = true;
175194
if (m_visible) {
176195
QueueShow(view);
177196
}
178197
}
179198

180199
void OnUnmounted(const winrt::Microsoft::ReactNative::ComponentView & /*view*/) noexcept {
200+
UnsubscribeFromThemeChanges();
181201
m_mounted = false;
182202
}
183203

204+
void SubscribeToThemeChanges(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
205+
UnsubscribeFromThemeChanges();
206+
207+
auto themeSource = view.Parent().as<winrt::Microsoft::ReactNative::Composition::ComponentView>();
208+
m_themeSource = winrt::make_weak(themeSource);
209+
m_theme = themeSource.Theme();
210+
const auto themeSubscriptionGeneration = m_themeSubscriptionGeneration;
211+
m_themeChangedToken = themeSource.ThemeChanged([wkThis = get_weak(), themeSubscriptionGeneration](
212+
const winrt::Windows::Foundation::IInspectable &sender,
213+
const winrt::Windows::Foundation::IInspectable & /*args*/) {
214+
auto theme = sender.as<winrt::Microsoft::ReactNative::Composition::ComponentView>().Theme();
215+
if (auto strongThis = wkThis.get()) {
216+
strongThis->m_reactContext.UIDispatcher().Post([wkThis, theme, themeSubscriptionGeneration]() {
217+
if (auto strongThis = wkThis.get()) {
218+
if (strongThis->m_themeSubscriptionGeneration != themeSubscriptionGeneration) {
219+
return;
220+
}
221+
strongThis->m_theme = theme;
222+
strongThis->UpdateTitleBarColors();
223+
}
224+
});
225+
}
226+
});
227+
}
228+
229+
void UnsubscribeFromThemeChanges() noexcept {
230+
if (m_themeChangedToken) {
231+
if (auto themeSource = m_themeSource.get()) {
232+
themeSource.ThemeChanged(m_themeChangedToken);
233+
}
234+
m_themeChangedToken = {};
235+
}
236+
m_themeSource = {};
237+
m_theme = nullptr;
238+
++m_themeSubscriptionGeneration;
239+
}
240+
184241
void AdjustWindowSize(const winrt::Microsoft::ReactNative::LayoutMetrics &layoutMetrics) noexcept {
185242
if (!m_rnWindow) {
186243
return;
@@ -318,6 +375,52 @@ struct ModalHostView : public winrt::implements<ModalHostView, winrt::Windows::F
318375

319376
titleBar.IconShowOptions(winrt::Microsoft::UI::Windowing::IconShowOptions::HideIconAndSystemMenu);
320377
}
378+
379+
UpdateTitleBarColors();
380+
}
381+
382+
void UpdateTitleBarColors() noexcept {
383+
if (!m_rnWindow || !m_theme || !m_localProps || m_localProps->hideTitleBar.value_or(false) ||
384+
!winrt::Microsoft::UI::Windowing::AppWindowTitleBar::IsCustomizationSupported()) {
385+
return;
386+
}
387+
388+
winrt::Windows::UI::Color background;
389+
winrt::Windows::UI::Color activeForeground;
390+
winrt::Windows::UI::Color inactiveForeground;
391+
winrt::Windows::UI::Color buttonHoverBackground;
392+
winrt::Windows::UI::Color buttonPressedBackground;
393+
winrt::Windows::UI::Color buttonPressedForeground;
394+
if (!m_theme.TryGetPlatformColor(L"SolidBackgroundFillColorBase", background) ||
395+
!m_theme.TryGetPlatformColor(L"TextFillColorPrimary", activeForeground) ||
396+
!m_theme.TryGetPlatformColor(L"TextFillColorSecondary", inactiveForeground) ||
397+
!m_theme.TryGetPlatformColor(L"ControlFillColorSecondary", buttonHoverBackground) ||
398+
!m_theme.TryGetPlatformColor(L"ControlFillColorTertiary", buttonPressedBackground) ||
399+
!m_theme.TryGetPlatformColor(L"ButtonForegroundPressed", buttonPressedForeground)) {
400+
return;
401+
}
402+
403+
// AppWindowTitleBar ignores alpha, so resolve RNW's translucent semantic text colors to opaque colors first.
404+
background.A = 0xFF;
405+
activeForeground = CompositeOverOpaqueBackground(activeForeground, background);
406+
inactiveForeground = CompositeOverOpaqueBackground(inactiveForeground, background);
407+
buttonHoverBackground = CompositeOverOpaqueBackground(buttonHoverBackground, background);
408+
buttonPressedBackground = CompositeOverOpaqueBackground(buttonPressedBackground, background);
409+
buttonPressedForeground = CompositeOverOpaqueBackground(buttonPressedForeground, buttonPressedBackground);
410+
411+
auto titleBar = m_rnWindow.AppWindow().TitleBar();
412+
titleBar.ForegroundColor(activeForeground);
413+
titleBar.BackgroundColor(background);
414+
titleBar.InactiveForegroundColor(inactiveForeground);
415+
titleBar.InactiveBackgroundColor(background);
416+
titleBar.ButtonForegroundColor(activeForeground);
417+
titleBar.ButtonBackgroundColor(background);
418+
titleBar.ButtonHoverForegroundColor(activeForeground);
419+
titleBar.ButtonHoverBackgroundColor(buttonHoverBackground);
420+
titleBar.ButtonPressedForegroundColor(buttonPressedForeground);
421+
titleBar.ButtonPressedBackgroundColor(buttonPressedBackground);
422+
titleBar.ButtonInactiveForegroundColor(inactiveForeground);
423+
titleBar.ButtonInactiveBackgroundColor(background);
321424
}
322425

323426
// creates a new modal window
@@ -453,6 +556,10 @@ struct ModalHostView : public winrt::implements<ModalHostView, winrt::Windows::F
453556
winrt::Microsoft::ReactNative::ReactNativeWindow m_rnWindow{nullptr};
454557
winrt::Microsoft::UI::Content::DesktopPopupSiteBridge m_popUp{nullptr};
455558
winrt::event_token m_appWindowClosingToken;
559+
winrt::weak_ref<winrt::Microsoft::ReactNative::Composition::ComponentView> m_themeSource;
560+
winrt::event_token m_themeChangedToken;
561+
uint64_t m_themeSubscriptionGeneration{0};
562+
winrt::Microsoft::ReactNative::Composition::Theme m_theme{nullptr};
456563
winrt::com_ptr<::Microsoft::ReactNativeSpecs::ModalHostViewProps> m_localProps{nullptr};
457564
};
458565

0 commit comments

Comments
 (0)