1515
1616namespace 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+
1834struct 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
3652struct 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