Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/settings-ui/Settings.UI/Helpers/TitleBarHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.UI;
using Microsoft.UI.Xaml;
using Windows.UI;

namespace Microsoft.PowerToys.Settings.UI.Helpers
{
/// <summary>
/// Helpers for theming the system caption buttons (minimize/maximize/close) of a window.
/// </summary>
public static class TitleBarHelper
{
/// <summary>
/// Applies the given element theme to a window's system caption buttons.
/// </summary>
/// <remarks>
/// Workaround for the AppWindow TitleBar not updating caption button colors to match the
/// app theme when the OS theme differs from the app theme or the theme changes at runtime.
/// Mirrors the helper used by the WinUI Gallery (https://github.com/microsoft/WinUI-Gallery).
/// </remarks>
public static void ApplySystemThemeToCaptionButtons(Window window, ElementTheme theme)
{
if (window?.AppWindow is null)
{
return;
}

var titleBar = window.AppWindow.TitleBar;
var foregroundColor = theme == ElementTheme.Dark ? Colors.White : Colors.Black;

titleBar.ButtonBackgroundColor = Colors.Transparent;
titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
titleBar.ButtonForegroundColor = foregroundColor;
titleBar.ButtonHoverForegroundColor = foregroundColor;
titleBar.ButtonInactiveForegroundColor = Colors.DarkGray;
titleBar.ButtonHoverBackgroundColor = theme == ElementTheme.Dark
? Color.FromArgb(24, 255, 255, 255)
: Color.FromArgb(24, 0, 0, 0);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,18 @@
<tkcontrols:MarkdownTextBlock
x:Name="ReleaseNotesMarkdown"
Config="{StaticResource ReleaseNotesMarkdownConfig}"
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
UseAutoLinks="True"
UseEmphasisExtras="True"
UseListExtras="True"
UsePipeTables="True"
UseTaskLists="True" />
<!-- Hidden helper used to resolve the accent brush for the active element theme (see ApplyMarkdownThemeWorkaround). -->
<TextBlock
x:Name="LinkBrushProvider"
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
IsHitTestVisible="False"
Visibility="Collapsed" />
</Grid>
</Grid>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using CommunityToolkit.WinUI.Controls;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using Microsoft.UI.Xaml.Navigation;

Expand All @@ -19,13 +21,45 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public sealed partial class ScoobeReleaseNotesPage : Page
{
private IList<PowerToysReleaseInfo> _currentReleases;
private string _releaseNotesMarkdownText;

/// <summary>
/// Initializes a new instance of the <see cref="ScoobeReleaseNotesPage"/> class.
/// </summary>
public ScoobeReleaseNotesPage()
{
this.InitializeComponent();

// Re-apply the markdown theme workaround when the theme changes at runtime so the
// headings/links stay readable after the user switches between light and dark.
this.ActualThemeChanged += OnActualThemeChanged;
this.Unloaded += OnUnloaded;
}

private void OnUnloaded(object sender, RoutedEventArgs e)
{
this.ActualThemeChanged -= OnActualThemeChanged;
this.Unloaded -= OnUnloaded;
}

private void OnActualThemeChanged(FrameworkElement sender, object args)
{
RefreshMarkdownTheme();
}

private void RefreshMarkdownTheme()
{
if (string.IsNullOrEmpty(_releaseNotesMarkdownText))
{
return;
}

ApplyMarkdownThemeWorkaround();

// The MarkdownTextBlock captures heading/link brushes when it renders, so re-set the
// text to force it to rebuild with the brushes for the now-active theme.
ReleaseNotesMarkdown.Text = string.Empty;
ReleaseNotesMarkdown.Text = _releaseNotesMarkdownText;
}

/// <summary>
Expand Down Expand Up @@ -128,7 +162,18 @@ private void DisplayReleaseNotes()
{
LoadingProgressRing.Visibility = Visibility.Collapsed;

// Workaround: the MarkdownTextBlock control captures its heading foreground
// brushes from Application.Current.Resources when its theme config is created,
// which resolves against the OS (application) theme rather than the app's
// selected theme. When the OS is Light but PowerToys is Dark (or vice versa),
// headings render with an unreadable color. Force the control's theme and
// reapply correctly-themed heading brushes before the markdown is rendered.
// TODO: Remove once the upstream control resolves brushes against the element theme.
// Upstream fix: https://github.com/CommunityToolkit/Labs-Windows/pull/785
ApplyMarkdownThemeWorkaround();

var (releaseNotesMarkdown, heroImageUrl) = ProcessReleaseNotesMarkdown(_currentReleases);
_releaseNotesMarkdownText = releaseNotesMarkdown;

// Set the Hero image if found
if (!string.IsNullOrEmpty(heroImageUrl))
Expand All @@ -150,6 +195,46 @@ private void DisplayReleaseNotes()
}
}

/// <summary>
/// Works around the <see cref="MarkdownTextBlock"/> control pinning its heading and link
/// brushes to the OS (application) theme instead of the element's selected theme, which makes
/// titles/links unreadable when the OS and PowerToys themes differ. Pins the control's theme and
/// reassigns the heading/link brushes resolved for the selected theme before the markdown renders.
/// TODO: Remove once the upstream control resolves brushes against the element theme.
/// Upstream fix: https://github.com/CommunityToolkit/Labs-Windows/pull/785
/// </summary>
private void ApplyMarkdownThemeWorkaround()
{
var elementTheme = App.IsDarkTheme() ? ElementTheme.Dark : ElementTheme.Light;
ReleaseNotesMarkdown.RequestedTheme = elementTheme;
LinkBrushProvider.RequestedTheme = elementTheme;

if (Resources["ReleaseNotesMarkdownConfig"] is MarkdownConfig config
&& config.Themes is MarkdownThemes themes)
{
// The control's Foreground is bound to TextFillColorPrimaryBrush via ThemeResource,
// so after setting RequestedTheme it resolves to the brush for the selected theme.
// Reuse it for the heading brushes, which the control would otherwise pin to the OS theme.
if (ReleaseNotesMarkdown.Foreground is Brush headingForeground)
{
themes.H1Foreground = headingForeground;
themes.H2Foreground = headingForeground;
themes.H3Foreground = headingForeground;
themes.H4Foreground = headingForeground;
themes.H5Foreground = headingForeground;
themes.H6Foreground = headingForeground;
}

// The link brush is likewise pinned to the OS theme's accent color, which can be
// unreadable when the app theme differs from the OS theme. Reapply the accent brush
// resolved for the selected theme using the hidden helper element.
if (LinkBrushProvider.Foreground is Brush linkForeground)
{
themes.LinkForeground = linkForeground;
}
}
}

private void Page_Loaded(object sender, RoutedEventArgs e)
{
DisplayReleaseNotes();
Expand Down
10 changes: 10 additions & 0 deletions src/settings-ui/Settings.UI/SettingsXAML/ScoobeWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ private void SetTitleBar()
this.ExtendsContentIntoTitleBar = true;
this.SetTitleBar(AppTitleBar);
Title = ResourceLoaderInstance.ResourceLoader.GetString("ScoobeWindow_Title");

// The built-in WinUI TitleBar does not tint the system caption buttons (min/max/close)
// to match the app's selected theme, so they can be unreadable when the OS theme differs
// from the PowerToys theme. Drive their colors from the window content's actual theme.
if (this.Content is FrameworkElement rootElement)
{
TitleBarHelper.ApplySystemThemeToCaptionButtons(this, rootElement.ActualTheme);
rootElement.ActualThemeChanged += (s, e) =>
TitleBarHelper.ApplySystemThemeToCaptionButtons(this, s.ActualTheme);
}
}

private void Window_Activated(object sender, WindowActivatedEventArgs args)
Expand Down
Loading