FluentAvalonia
FluentAvalonia copied to clipboard
List Item blue dot trail from selection indicator
Describe the bug When changing the selected item in a listbox, tiny blue dots remain at the position the selection indicator was.
Screenshots
Desktop/Platform (please complete the following information):
- OS: Windows 11
- FluentAvalonia Version 1.3.1
- Avalonia Version 0.10.13
Additional context
It's either a Skia anti-aliasing thing or a deferred renderer dirty rect thing (or both). I'm waiting for the new renderer to see if the improvements in that fix it before I try to see if there's anything I can do.
I've already tried adding a margin to make it not sit against the edge of the LBI, but it didn't help: https://github.com/amwx/FluentAvalonia/blob/01dfeb05de3e8d0ddea482b5601727cf44d55493/FluentAvalonia/Styling/BasicControls/ListBoxStyles.axaml#L78-L87
This one looks interesting. I don't think the problem is with the dirty rect at first look.
-
It looks like there are actually 4 rendering artifacts (marked in red) that shouldn't be there at all. Since they shouldn't be there anyway that portion of the view is never invalidated to re-render so the artifacts remain.
-
Additionally, is the selection pill supposed to be rendered outside the width of the ListBoxItem? I don't have Windows 11 at the moment but all the selection pills I have seen are left aligned with the grey background of the ListBoxItem. This selection pill is horizontally centered on the left edge of the grey background itself so half of it sticks out past the edge.
Waiting for the new renderer is probably smart here.
- Additionally, is the selection pill supposed to be rendered outside the width of the ListBoxItem?
No. It should be aligned with the left edge of pointer background. The template for ListViewItem is a bit of a special case - since the actual style uses the ListViewItemPresenter
for whatever reason MS does this, but they didn't add that template to the xaml file. The "expanded" style they did include (which essentially unwraps the presenter) doesn't include the selection indicator, so I had to manually do that. To fix, the Margin on the selection indicator needs to be {2,0,0,0}, but I just tried that and the artifacts got even worse - so it needs to wait.
I believe this artifact issue is related to why we don't want anti-aliasing rounded rect clipping on Skia. It isn't just the selection indicator here doing it, I have it happening elsewhere too (outside this library) where it's even worse. Remove the corner radius, and it disappears. So you're right, it's not dirty rects - it's Skia (or SkiaSharp) being bad.
I believe this artifact issue is related to why we don't want anti-aliasing rounded rect clipping on Skia.
Yes, you're very likely correct. Hopefully an update to SkiaSharp is released sometime soon. Everyone has been waiting on it for what seems like a year now. Maui is almost done so I hope development picks up again.
Also, just for reference, here is a WinUI 2 style for the selection pill indicator of a ListViewItem. This was based on the ComboBoxItem style since, as you said, Microsoft doesn't currently release what they have in WinUI 3 for this style. It uses the ContentPresenter and seems to work well enough for app needs. Certain things like Padding are app specific but you might find it useful for whatever reason.
Click to Expand Code
<!--
A customized ListViewItem style that solves the issue of Text Foreground color in child controls.
Fluent v2 changes the selected item background contrast so much the text Foreground color must be inverted.
That causes a problem for child controls that set their own text Foreground (like DatePicker).
This wasn't an issue in Fluent v1 where the background contrast was never so high.
The fix for this is to use a style heavily derived from ComboBoxItem with the pill indicator and a gray background.
This style was created with only a few modifications from the DefaultComboBoxItemStyle to make it work for ListViewItem.
See: https://github.com/microsoft/microsoft-ui-xaml/issues/6469
Source: https://github.com/microsoft/microsoft-ui-xaml/blob/01eea310f89b72109a36de6537e3357fb2421556/dev/ComboBox/ComboBox_themeresources.xaml#L778
-->
<Style
TargetType="ListViewItem"
x:Key="PillListViewItemStyle">
<Setter
Property="Foreground"
Value="{ThemeResource ComboBoxItemForeground}" />
<Setter
Property="Background"
Value="{ThemeResource ComboBoxItemBackground}" />
<Setter
Property="Padding"
Value="12,3,12,3" />
<Setter
Property="HorizontalContentAlignment"
Value="Stretch" />
<Setter
Property="VerticalContentAlignment"
Value="Stretch" />
<Setter
Property="UseSystemFocusVisuals"
Value="True" />
<Setter
Property="FocusVisualMargin"
Value="-3" />
<Setter
Property="CornerRadius"
Value="{StaticResource ComboBoxItemCornerRadius}" />
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="ListViewItem">
<Grid
x:Name="LayoutRoot"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Control.IsTemplateFocusTarget="True">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup
x:Name="CommonStates">
<VisualState
x:Name="Normal" />
<VisualState
x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemBackgroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemBorderBrushPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemForegroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState
x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemBackgroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemBorderBrushDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState
x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemBackgroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemBorderBrushPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemForegroundPressed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState
x:Name="Selected">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="Pill"
Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="1" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemBackgroundSelected}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemBorderBrushSelected}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemForegroundSelected}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState
x:Name="PointerOverSelected">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="Pill"
Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="1" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemBackgroundSelectedPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemBorderBrushSelectedPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemForegroundSelectedPointerOver}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState
x:Name="PressedSelected">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="Pill"
Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="1" />
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="PillTransform"
Storyboard.TargetProperty="ScaleY">
<SplineDoubleKeyFrame
KeyTime="{StaticResource ComboBoxItemScaleAnimationDuration}"
Value="{StaticResource ComboBoxItemPillMinScale}"
KeySpline="0,0,0,1" />
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemBackgroundSelectedPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="LayoutRoot"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemBorderBrushSelectedPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{ThemeResource ComboBoxItemForegroundSelectedPressed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle
x:Name="Pill"
Style="{StaticResource ListViewItemPill}"
Opacity="0"
RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<CompositeTransform
x:Name="PillTransform"
ScaleY="1" />
</Rectangle.RenderTransform>
</Rectangle>
<ContentPresenter
x:Name="ContentPresenter"
Content="{TemplateBinding Content}"
ContentTransitions="{TemplateBinding ContentTransitions}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Foreground="{TemplateBinding Foreground}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="{TemplateBinding Padding}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- BasedOn Source: https://github.com/microsoft/microsoft-ui-xaml/blob/01eea310f89b72109a36de6537e3357fb2421556/dev/ComboBox/ComboBox_themeresources.xaml#L372 -->
<Style
x:Key="ListViewItemPill"
TargetType="Rectangle">
<Setter
Property="HorizontalAlignment"
Value="Left" />
<Setter
Property="VerticalAlignment"
Value="Center" />
<Setter
Property="Height"
Value="32" />
<Setter
Property="Width"
Value="3" />
<Setter
Property="Fill"
Value="{ThemeResource ComboBoxItemPillFillBrush}" />
<Setter
Property="RadiusX"
Value="1.5" />
<Setter
Property="RadiusY"
Value="1.5" />
</Style>