Browse Source

Fixed the dark theme.

pull/21771/head
Mike James 3 months ago
parent
commit
afb28ecb92
  1. BIN
      samples/ControlCatalog/Assets/Icon.png
  2. 8
      src/Avalonia.Themes.Fluent2/Accents/BaseResources.xaml
  3. 228
      src/Avalonia.Themes.Fluent2/Controls/DrawerPage.xaml
  4. 12
      src/Avalonia.Themes.Fluent2/Controls/NavigationPage.xaml
  5. 27
      src/Avalonia.Themes.Fluent2/Controls/TabbedPage.xaml
  6. 53
      tests/Avalonia.Themes.Fluent2.UnitTests/KeyParityTests.cs

BIN
samples/ControlCatalog/Assets/Icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

8
src/Avalonia.Themes.Fluent2/Accents/BaseResources.xaml

@ -99,8 +99,8 @@
Color="{StaticResource SystemChromeMediumLowColor}" />
<SolidColorBrush x:Key="SystemControlDisabledListMediumBrush" Color="{StaticResource SystemListMediumColor}" />
<SolidColorBrush x:Key="SystemControlDisabledTransparentBrush" Color="Transparent" />
<SolidColorBrush x:Key="SystemControlFocusVisualPrimaryBrush" Color="{DynamicResource FocusStrokeColorOuter}" />
<SolidColorBrush x:Key="SystemControlFocusVisualSecondaryBrush" Color="{DynamicResource FocusStrokeColorInner}" />
<SolidColorBrush x:Key="SystemControlFocusVisualPrimaryBrush" Color="{StaticResource FocusStrokeColorOuter}" />
<SolidColorBrush x:Key="SystemControlFocusVisualSecondaryBrush" Color="{StaticResource FocusStrokeColorInner}" />
<SolidColorBrush x:Key="SystemControlRevealFocusVisualBrush" Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="SystemControlForegroundAccentBrush" Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="SystemControlForegroundAltHighBrush" Color="{StaticResource SystemAltHighColor}" />
@ -260,8 +260,8 @@
Color="{StaticResource SystemChromeMediumLowColor}" />
<SolidColorBrush x:Key="SystemControlDisabledListMediumBrush" Color="{StaticResource SystemListMediumColor}" />
<SolidColorBrush x:Key="SystemControlDisabledTransparentBrush" Color="Transparent" />
<SolidColorBrush x:Key="SystemControlFocusVisualPrimaryBrush" Color="{DynamicResource FocusStrokeColorOuter}" />
<SolidColorBrush x:Key="SystemControlFocusVisualSecondaryBrush" Color="{DynamicResource FocusStrokeColorInner}" />
<SolidColorBrush x:Key="SystemControlFocusVisualPrimaryBrush" Color="{StaticResource FocusStrokeColorOuter}" />
<SolidColorBrush x:Key="SystemControlFocusVisualSecondaryBrush" Color="{StaticResource FocusStrokeColorInner}" />
<SolidColorBrush x:Key="SystemControlRevealFocusVisualBrush" Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="SystemControlForegroundAccentBrush" Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="SystemControlForegroundAltHighBrush" Color="{StaticResource SystemAltHighColor}" />

228
src/Avalonia.Themes.Fluent2/Controls/DrawerPage.xaml

@ -10,41 +10,140 @@
</DrawerPage>
</Design.PreviewWith>
<!-- WinUI 3 mapping (NavigationView.xaml / NavigationView_themeresources.xaml):
DrawerPage plays the role of NavigationView. Display modes map to pane display modes -
Inline = Left (expanded), CompactInline/CompactOverlay = LeftCompact, Overlay = LeftMinimal.
The three anatomy rules ported from WinUI:
1. The hamburger toggle lives at the top of the pane (PaneToggleButtonStyle, 40x36,
margin 4,2 inside a 0,4 button-holder row), not in a chrome bar over the content.
Only LeftMinimal (Overlay) keeps a transparent toggle strip above the content.
2. The content area is a "layer" card: LayerFillColorDefault fill, CardStrokeColorDefault
1,1,0,0 stroke, OverlayCornerRadius on the pane-side top corner. Minimal/top-nav modes
flatten it to a 0,1,0,0 hairline with no rounding.
3. The pane is transparent when it does not overlay content (PaneNotOverlaying state) and
uses the in-app acrylic fill (solid fallback here) while overlaying. No smoke dim -
light dismiss is a transparent hit-target, as in WinUI. -->
<!-- Fluent E700 GlobalNavigationButton hamburger, drawn on a 24x24 grid. -->
<StreamGeometry x:Key="DrawerPaneButtonIcon">M3 17h18a1 1 0 0 1 .117 1.993L21 19H3a1 1 0 0 1-.117-1.993L3 17h18H3Zm0-6 18-.002a1 1 0 0 1 .117 1.993l-.117.007L3 13a1 1 0 0 1-.117-1.993L3 11l18-.002L3 11Zm0-6h18a1 1 0 0 1 .117 1.993L21 7H3a1 1 0 0 1-.117-1.993L3 5h18H3Z</StreamGeometry>
<!-- Kept from v1 for resource-key parity; the strip is transparent in the WinUI 3 look. -->
<x:Double x:Key="NavigationControlNavBarHeight">48</x:Double>
<SolidColorBrush x:Key="NavigationControlNavBarBackground" Color="{DynamicResource SolidBackgroundFillColorBaseAlt}" />
<SolidColorBrush x:Key="NavigationControlNavBarBackground" Color="Transparent" />
<!-- WinUI 3 NavigationView metrics, verbatim. -->
<x:Double x:Key="PaneToggleButtonWidth">40</x:Double>
<x:Double x:Key="PaneToggleButtonHeight">36</x:Double>
<Thickness x:Key="NavigationViewItemButtonMargin">4,2</Thickness>
<Thickness x:Key="NavigationViewButtonHolderGridMargin">0,4</Thickness>
<Thickness x:Key="NavigationViewContentGridBorderThickness">1,1,0,0</Thickness>
<CornerRadius x:Key="NavigationViewContentGridCornerRadius">8,0,0,0</CornerRadius>
<Thickness x:Key="NavigationViewMinimalContentGridBorderThickness">0,1,0,0</Thickness>
<CornerRadius x:Key="NavigationViewMinimalContentGridCornerRadius">0</CornerRadius>
<Thickness x:Key="TopNavigationViewContentGridBorderThickness">0,1,0,0</Thickness>
<CornerRadius x:Key="TopNavigationViewContentGridCornerRadius">0</CornerRadius>
<!-- WinUI 3 NavigationView brushes. Colors track the palette tokens so ColorPaletteResources
and token overrides flow through. -->
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<StaticResource x:Key="NavigationViewContentBackground" ResourceKey="LayerFillColorDefaultBrush" />
<StaticResource x:Key="NavigationViewContentGridBorderBrush" ResourceKey="CardStrokeColorDefaultBrush" />
<StaticResource x:Key="NavigationViewExpandedPaneBackground" ResourceKey="SubtleFillColorTransparentBrush" />
<StaticResource x:Key="NavigationViewDefaultPaneBackground" ResourceKey="AcrylicInAppFillColorDefaultBrush" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<StaticResource x:Key="NavigationViewContentBackground" ResourceKey="LayerFillColorDefaultBrush" />
<StaticResource x:Key="NavigationViewContentGridBorderBrush" ResourceKey="CardStrokeColorDefaultBrush" />
<StaticResource x:Key="NavigationViewExpandedPaneBackground" ResourceKey="SubtleFillColorTransparentBrush" />
<StaticResource x:Key="NavigationViewDefaultPaneBackground" ResourceKey="AcrylicInAppFillColorDefaultBrush" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<!-- WinUI PaneToggleButtonStyle: 40x36 minimum, transparent rest state, subtle fills on
hover/press, ControlCornerRadius, 16px glyph. Checked (= drawer open) renders like rest,
matching the stateless WinUI pane toggle Button. -->
<ControlTheme x:Key="Fluent2DrawerPaneToggleButton" TargetType="ToggleButton">
<Setter Property="Background" Value="{DynamicResource SubtleFillColorTransparentBrush}" />
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimaryBrush}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
<Setter Property="MinWidth" Value="{DynamicResource PaneToggleButtonWidth}" />
<Setter Property="MinHeight" Value="{DynamicResource PaneToggleButtonHeight}" />
<!-- NavigationViewButtonHolderGridMargin 0,4 + NavigationViewItemButtonMargin 4,2 -->
<Setter Property="Margin" Value="4,6" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
</ControlTemplate>
</Setter>
<Style Selector="^:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SubtleFillColorSecondaryBrush}" />
</Style>
<Style Selector="^:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SubtleFillColorTertiaryBrush}" />
</Style>
<Style Selector="^:pressed">
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
</Style>
<Style Selector="^:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ControlFillColorDisabledBrush}" />
</Style>
<Style Selector="^:disabled">
<Setter Property="Foreground" Value="{DynamicResource TextFillColorDisabledBrush}" />
</Style>
</ControlTheme>
<ControlTheme x:Key="{x:Type DrawerPage}" TargetType="DrawerPage">
<Setter Property="Background" Value="{DynamicResource SolidBackgroundFillColorBaseBrush}" />
<!-- Content chrome = WinUI ContentGrid: layer fill, card stroke, rounded pane-side corner.
Flows onto PART_ContentPresenter through the standard template bindings. -->
<Setter Property="Background" Value="{DynamicResource NavigationViewContentBackground}" />
<Setter Property="BorderBrush" Value="{DynamicResource NavigationViewContentGridBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource NavigationViewContentGridBorderThickness}" />
<Setter Property="CornerRadius" Value="{DynamicResource NavigationViewContentGridCornerRadius}" />
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimaryBrush}" />
<!-- Smoke layer shown behind the drawer in the overlay display modes. -->
<Setter Property="BackdropBrush" Value="{DynamicResource SmokeFillColorDefaultBrush}" />
<!-- WinUI overlay panes light-dismiss without dimming; a transparent brush keeps the
click-outside-to-close hit target without the smoke. -->
<Setter Property="BackdropBrush" Value="Transparent" />
<Setter Property="Template">
<ControlTemplate>
<SplitView Name="PART_SplitView"
DisplayMode="{TemplateBinding DisplayMode}"
OpenPaneLength="{TemplateBinding DrawerLength}"
CompactPaneLength="{TemplateBinding CompactDrawerLength}"
PaneBackground="{Binding DrawerBackground, RelativeSource={RelativeSource TemplatedParent}}"
PaneBackground="{Binding DrawerBackground, RelativeSource={RelativeSource TemplatedParent}, TargetNullValue={StaticResource NavigationViewExpandedPaneBackground}}"
IsPaneOpen="{Binding IsOpen, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}">
<SplitView.Pane>
<DockPanel Background="{TemplateBinding DrawerBackground}">
<!-- Compact-mode toggle: visible only in CompactOverlay/CompactInline. -->
<DockPanel>
<!-- WinUI TogglePaneButton: anchored at the pane top in every left display mode;
in the compact strip it doubles as the strip toggle. -->
<ToggleButton Name="PART_CompactPaneToggle"
Theme="{StaticResource Fluent2DrawerPaneToggleButton}"
DockPanel.Dock="Top"
IsChecked="{Binding #PART_SplitView.IsPaneOpen, Mode=TwoWay}"
Background="Transparent"
IsVisible="False"
HorizontalAlignment="Left"
Width="{TemplateBinding CompactDrawerLength}"
Height="{TemplateBinding CompactDrawerLength}"
AutomationProperties.Name="Toggle navigation drawer"
ToolTip.Tip="Toggle navigation drawer">
<Panel>
<PathIcon
IsVisible="{Binding DrawerIcon, RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static ObjectConverters.IsNull}}"
Data="{DynamicResource DrawerPaneButtonIcon}"
Width="16"
Height="16"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<ContentPresenter
@ -78,27 +177,17 @@
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<DockPanel Name="PART_ContentDock">
<!-- LeftMinimal-only toggle strip: transparent, no divider; the layer card's
0,1,0,0 hairline provides the separation, as in WinUI. -->
<Border
Name="PART_TopBar"
Padding="5"
Height="{DynamicResource NavigationControlNavBarHeight}"
Background="{DynamicResource NavigationControlNavBarBackground}"
BackgroundSizing="InnerBorderEdge"
BorderBrush="{DynamicResource DividerStrokeColorDefaultBrush}"
BorderThickness="0,0,0,1"
DockPanel.Dock="Top">
<DockPanel HorizontalAlignment="Stretch">
<DockPanel.Styles>
<Styles>
<Style Selector="Button:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background"
Value="Transparent" />
</Style>
</Styles>
</DockPanel.Styles>
<ToggleButton x:Name="PART_PaneButton"
Theme="{StaticResource Fluent2DrawerPaneToggleButton}"
IsChecked="{Binding #PART_SplitView.IsPaneOpen, Mode=TwoWay}"
Background="Transparent"
DockPanel.Dock="Left"
AutomationProperties.Name="Toggle navigation drawer"
ToolTip.Tip="Toggle navigation drawer">
@ -106,6 +195,8 @@
<PathIcon
IsVisible="{Binding DrawerIcon, RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static ObjectConverters.IsNull}}"
Data="{DynamicResource DrawerPaneButtonIcon}"
Width="16"
Height="16"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<ContentPresenter
@ -130,26 +221,14 @@
</Border>
<Border
Name="PART_BottomBar"
Padding="5"
Height="{DynamicResource NavigationControlNavBarHeight}"
Background="{DynamicResource NavigationControlNavBarBackground}"
BackgroundSizing="InnerBorderEdge"
BorderBrush="{DynamicResource DividerStrokeColorDefaultBrush}"
BorderThickness="0,1,0,0"
DockPanel.Dock="Bottom"
IsVisible="False">
<DockPanel HorizontalAlignment="Stretch">
<DockPanel.Styles>
<Styles>
<Style Selector="Button:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background"
Value="Transparent" />
</Style>
</Styles>
</DockPanel.Styles>
<ToggleButton x:Name="PART_BottomPaneButton"
Theme="{StaticResource Fluent2DrawerPaneToggleButton}"
IsChecked="{Binding #PART_SplitView.IsPaneOpen, Mode=TwoWay}"
Background="Transparent"
DockPanel.Dock="Left"
AutomationProperties.Name="Toggle navigation drawer"
ToolTip.Tip="Toggle navigation drawer">
@ -157,6 +236,8 @@
<PathIcon
IsVisible="{Binding DrawerIcon, RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static ObjectConverters.IsNull}}"
Data="{DynamicResource DrawerPaneButtonIcon}"
Width="16"
Height="16"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<ContentPresenter
@ -198,6 +279,48 @@
</ControlTemplate>
</Setter>
<!-- Pane surface while overlaying content (WinUI PaneOverlaying state):
in-app acrylic fill (solid fallback). The SplitView theme supplies the rounded free
edge and flyout stroke. A user-set DrawerBackground wins over both states. -->
<Style Selector="^[DisplayMode=Overlay] /template/ SplitView#PART_SplitView:open">
<Setter Property="PaneBackground" Value="{DynamicResource NavigationViewDefaultPaneBackground}" />
</Style>
<Style Selector="^[DisplayMode=CompactOverlay] /template/ SplitView#PART_SplitView:open">
<Setter Property="PaneBackground" Value="{DynamicResource NavigationViewDefaultPaneBackground}" />
</Style>
<!-- The toggle strip exists only in the Overlay (LeftMinimal) display mode; every other
mode hosts the toggle inside the pane. -->
<Style Selector="^[DisplayMode=Inline] /template/ Border#PART_TopBar">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^[DisplayMode=CompactInline] /template/ Border#PART_TopBar">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^[DisplayMode=CompactOverlay] /template/ Border#PART_TopBar">
<Setter Property="IsVisible" Value="False" />
</Style>
<!-- Minimal (Overlay) content chrome: flat hairline, no rounding (WinUI Minimal state). -->
<Style Selector="^[DisplayMode=Overlay]">
<Setter Property="BorderThickness" Value="{DynamicResource NavigationViewMinimalContentGridBorderThickness}" />
<Setter Property="CornerRadius" Value="{DynamicResource NavigationViewMinimalContentGridCornerRadius}" />
</Style>
<!-- Placement variants mirror the pane-side corner/stroke of the content card. -->
<Style Selector="^:placement-right">
<Setter Property="BorderThickness" Value="0,1,1,0" />
<Setter Property="CornerRadius" Value="0,8,0,0" />
</Style>
<Style Selector="^:placement-top">
<Setter Property="BorderThickness" Value="{DynamicResource TopNavigationViewContentGridBorderThickness}" />
<Setter Property="CornerRadius" Value="{DynamicResource TopNavigationViewContentGridCornerRadius}" />
</Style>
<Style Selector="^:placement-bottom">
<Setter Property="BorderThickness" Value="0,0,0,1" />
<Setter Property="CornerRadius" Value="0" />
</Style>
<Style Selector="^:placement-right /template/ ToggleButton#PART_PaneButton">
<Setter Property="DockPanel.Dock" Value="Right" />
</Style>
@ -210,7 +333,10 @@
<Setter Property="IsVisible" Value="True" />
</Style>
<!-- Bottom placement + compact modes: pane compact strip provides toggle, hide bottom bar -->
<!-- Bottom placement + non-minimal modes: the pane hosts the toggle, hide the bottom bar -->
<Style Selector="^:placement-bottom[DisplayMode=Inline] /template/ Border#PART_BottomBar">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^:placement-bottom[DisplayMode=CompactOverlay] /template/ Border#PART_BottomBar">
<Setter Property="IsVisible" Value="False" />
</Style>
@ -218,7 +344,7 @@
<Setter Property="IsVisible" Value="False" />
</Style>
<!-- Horizontal pane (Bottom/Top): compact toggle must dock Left so drawer content can fill the rest -->
<!-- Horizontal pane (Bottom/Top): pane toggle must dock Left so drawer content can fill the rest -->
<Style Selector="^:placement-bottom /template/ ToggleButton#PART_CompactPaneToggle">
<Setter Property="DockPanel.Dock" Value="Left" />
</Style>
@ -226,39 +352,29 @@
<Setter Property="DockPanel.Dock" Value="Left" />
</Style>
<!-- No toggle when the drawer cannot be toggled. -->
<Style Selector="^[DrawerBehavior=Locked] /template/ Border#PART_TopBar">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^[DrawerBehavior=Disabled] /template/ Border#PART_TopBar">
<Style Selector="^[DrawerBehavior=Locked] /template/ ToggleButton#PART_CompactPaneToggle">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^:detail-is-navpage /template/ Border#PART_TopBar">
<Style Selector="^[DrawerBehavior=Disabled] /template/ Border#PART_TopBar">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^[DisplayMode=CompactOverlay] /template/ Border#PART_TopBar">
<Style Selector="^[DrawerBehavior=Disabled] /template/ ToggleButton#PART_CompactPaneToggle">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^[DisplayMode=CompactOverlay] /template/ ToggleButton#PART_CompactPaneToggle">
<Setter Property="IsVisible" Value="True" />
</Style>
<Style Selector="^[DisplayMode=CompactInline] /template/ Border#PART_TopBar">
<Style Selector="^:detail-is-navpage /template/ Border#PART_TopBar">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^[DisplayMode=CompactInline] /template/ ToggleButton#PART_CompactPaneToggle">
<Setter Property="IsVisible" Value="True" />
</Style>
<Style Selector="^[IsInNavigationPage=True] /template/ Border#PART_TopBar">
<Setter Property="IsVisible" Value="False" />
</Style>
<!-- NavigationPage already hosts a hamburger toggle — suppress the compact pane toggle. -->
<!-- NavigationPage already hosts a hamburger toggle — suppress the pane toggle. -->
<Style Selector="^:detail-is-navpage /template/ ToggleButton#PART_CompactPaneToggle">
<Setter Property="IsVisible" Value="False" />
</Style>

12
src/Avalonia.Themes.Fluent2/Controls/NavigationPage.xaml

@ -1,5 +1,15 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<StaticResource x:Key="NavigationBarBackground" ResourceKey="SolidBackgroundFillColorBaseAltBrush" />
<StaticResource x:Key="NavigationBarForeground" ResourceKey="TextFillColorPrimaryBrush" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<StaticResource x:Key="NavigationBarBackground" ResourceKey="SolidBackgroundFillColorBaseAltBrush" />
<StaticResource x:Key="NavigationBarForeground" ResourceKey="TextFillColorPrimaryBrush" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<Design.PreviewWith>
<Border Width="400" Height="600">
<NavigationPage />
@ -10,8 +20,6 @@
<StreamGeometry x:Key="NavigationPageBackIcon">M12.7347,4.20949 C13.0332,3.92233 13.508,3.93153 13.7952,4.23005 C14.0823,4.52857 14.0731,5.00335 13.7746,5.29051 L5.50039,13.25 L24.2532,13.25 C24.6674,13.25 25.0032,13.5858 25.0032,13.9999982 C25.0032,14.4142 24.6674,14.75 24.2532,14.75 L5.50137,14.75 L13.7746,22.7085 C14.0731,22.9957 14.0823,23.4705 13.7952,23.769 C13.508,24.0675 13.0332,24.0767 12.7347,23.7896 L3.30673,14.7202 C2.89776,14.3268 2.89776,13.6723 3.30673,13.2788 L12.7347,4.20949 Z</StreamGeometry>
<SolidColorBrush x:Key="NavigationBarBackground" Color="{DynamicResource SolidBackgroundFillColorBaseAlt}" />
<SolidColorBrush x:Key="NavigationBarForeground" Color="{DynamicResource TextFillColorPrimary}" />
<ControlTheme x:Key="{x:Type NavigationPage}" TargetType="NavigationPage">
<!-- Transparent keeps the hit-test surface active for the back-swipe gesture edge area. -->

27
src/Avalonia.Themes.Fluent2/Controls/TabbedPage.xaml

@ -1,5 +1,25 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<StaticResource x:Key="TabbedPageTabItemHeaderBackgroundUnselectedPointerOver" ResourceKey="SubtleFillColorSecondaryBrush" />
<StaticResource x:Key="TabbedPageTabItemHeaderBackgroundUnselectedPressed" ResourceKey="SubtleFillColorTertiaryBrush" />
<StaticResource x:Key="TabbedPageTabItemHeaderForegroundUnselected" ResourceKey="TextFillColorSecondaryBrush" />
<StaticResource x:Key="TabbedPageTabItemHeaderForegroundSelected" ResourceKey="TextFillColorPrimaryBrush" />
<StaticResource x:Key="TabbedPageTabItemHeaderForegroundDisabled" ResourceKey="TextFillColorDisabledBrush" />
<StaticResource x:Key="TabbedPageTabStripBackground" ResourceKey="SolidBackgroundFillColorBaseAltBrush" />
<StaticResource x:Key="TabbedPageTabStripBorderBrush" ResourceKey="DividerStrokeColorDefaultBrush" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<StaticResource x:Key="TabbedPageTabItemHeaderBackgroundUnselectedPointerOver" ResourceKey="SubtleFillColorSecondaryBrush" />
<StaticResource x:Key="TabbedPageTabItemHeaderBackgroundUnselectedPressed" ResourceKey="SubtleFillColorTertiaryBrush" />
<StaticResource x:Key="TabbedPageTabItemHeaderForegroundUnselected" ResourceKey="TextFillColorSecondaryBrush" />
<StaticResource x:Key="TabbedPageTabItemHeaderForegroundSelected" ResourceKey="TextFillColorPrimaryBrush" />
<StaticResource x:Key="TabbedPageTabItemHeaderForegroundDisabled" ResourceKey="TextFillColorDisabledBrush" />
<StaticResource x:Key="TabbedPageTabStripBackground" ResourceKey="SolidBackgroundFillColorBaseAltBrush" />
<StaticResource x:Key="TabbedPageTabStripBorderBrush" ResourceKey="DividerStrokeColorDefaultBrush" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<Design.PreviewWith>
<Border Width="400" Height="400">
<TabbedPage>
@ -19,16 +39,11 @@
<!-- Backgrounds -->
<SolidColorBrush x:Key="TabbedPageTabItemHeaderBackgroundUnselected" Color="Transparent" />
<SolidColorBrush x:Key="TabbedPageTabItemHeaderBackgroundSelected" Color="Transparent" />
<SolidColorBrush x:Key="TabbedPageTabItemHeaderBackgroundUnselectedPointerOver" Color="{DynamicResource SubtleFillColorSecondary}" />
<SolidColorBrush x:Key="TabbedPageTabItemHeaderBackgroundSelectedPointerOver" Color="Transparent" />
<SolidColorBrush x:Key="TabbedPageTabItemHeaderBackgroundUnselectedPressed" Color="{DynamicResource SubtleFillColorTertiary}" />
<SolidColorBrush x:Key="TabbedPageTabItemHeaderBackgroundSelectedPressed" Color="Transparent" />
<SolidColorBrush x:Key="TabbedPageTabItemHeaderBackgroundDisabled" Color="Transparent" />
<!-- Foregrounds (override via TabbedPageTabItemHeaderForegroundSelected / TabbedPageTabItemHeaderForegroundUnselected resources) -->
<SolidColorBrush x:Key="TabbedPageTabItemHeaderForegroundUnselected" Color="{DynamicResource TextFillColorSecondary}" />
<SolidColorBrush x:Key="TabbedPageTabItemHeaderForegroundSelected" Color="{DynamicResource TextFillColorPrimary}" />
<SolidColorBrush x:Key="TabbedPageTabItemHeaderForegroundDisabled" Color="{DynamicResource TextFillColorDisabled}" />
<x:Double x:Key="TabbedPageTabItemHeaderPipeThickness">3</x:Double>
<x:Double x:Key="TabbedPageTabItemHeaderVerticalPipeHeight">18</x:Double>
@ -48,10 +63,8 @@
<!-- Tab strip (bar) styling. Divider edge defaults to the top (Bottom placement);
the other placements are corrected via TabStripPlacement styles below. -->
<SolidColorBrush x:Key="TabbedPageTabStripBackground" Color="{DynamicResource SolidBackgroundFillColorBaseAlt}" />
<CornerRadius x:Key="TabbedPageTabStripCornerRadius">0</CornerRadius>
<Thickness x:Key="TabbedPageTabStripBorderThickness">0,1,0,0</Thickness>
<SolidColorBrush x:Key="TabbedPageTabStripBorderBrush" Color="{DynamicResource DividerStrokeColorDefault}" />
<Thickness x:Key="TabbedPageTabStripMargin">0</Thickness>
<Thickness x:Key="TabbedPageTabStripPadding">0</Thickness>

53
tests/Avalonia.Themes.Fluent2.UnitTests/KeyParityTests.cs

@ -112,6 +112,59 @@ public class KeyParityTests
"Compact density keys missing in Fluent2: " + string.Join(", ", missing));
}
[AvaloniaFact]
public void Window_scoped_dark_variant_resolves_same_values_as_theme_dark()
{
// Regression guard: brushes defined outside ThemeDictionaries that wrap
// variant-scoped colors anchor to the app variant instead of the consumer's
// scope. Every key must resolve identically whether dark is requested at
// theme level or via a window-scoped RequestedThemeVariant override.
var theme = new Fluent2Theme();
var app = Application.Current!;
app.Styles.Add(theme);
var window = new Window
{
RequestedThemeVariant = ThemeVariant.Dark,
Content = new ContentControl(),
};
window.Show();
try
{
var control = (ContentControl)window.Content!;
var problems = new List<string>();
foreach (var key in ThemeResourceWalker.CollectKeys(theme))
{
if (!((IResourceNode)theme).TryGetResource(key, ThemeVariant.Dark, out var expected))
continue;
if (expected is not (IBrush or Color))
continue;
control.TryFindResource(key, control.ActualThemeVariant, out var actual);
var expectedText = Describe(expected);
var actualText = Describe(actual);
if (expectedText != actualText)
problems.Add($"{key}: theme-dark={expectedText} window-dark={actualText}");
}
Assert.True(problems.Count == 0,
$"{problems.Count} keys resolve differently under window-scoped dark:\n" +
string.Join("\n", problems.Take(30)));
}
finally
{
window.Close();
app.Styles.Remove(theme);
}
}
private static string Describe(object? value) => value switch
{
ISolidColorBrush b => $"solid:{b.Color}",
Color c => $"color:{c}",
IBrush b => $"{b.GetType().Name}:{b.GetHashCode()}",
null => "null",
_ => value.GetType().Name,
};
private static ResourceDictionary GetCompactStyles(object theme)
{
// Both themes stash the keyed CompactStyles dictionary in a private field;

Loading…
Cancel
Save