Browse Source

Merge branch 'master' of https://github.com/AvaloniaUI/Avalonia

pull/4108/head
amwx 6 years ago
parent
commit
994c49432b
  1. 4
      readme.md
  2. 1
      samples/ControlCatalog/MainView.xaml
  3. 7
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml
  4. 34
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
  5. 35
      samples/ControlCatalog/Pages/ScrollViewerPage.xaml
  6. 65
      samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs
  7. 13
      samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs
  8. 33
      src/Avalonia.Base/Utilities/MathUtilities.cs
  9. 6
      src/Avalonia.Controls/Grid.cs
  10. 20
      src/Avalonia.Controls/IScrollAnchorProvider.cs
  11. 198
      src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
  12. 27
      src/Avalonia.Controls/Primitives/HeaderedContentControl.cs
  13. 123
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  14. 8
      src/Avalonia.Controls/Repeater/ItemsRepeater.cs
  15. 7
      src/Avalonia.Controls/Repeater/RepeaterLayoutContext.cs
  16. 27
      src/Avalonia.Controls/Repeater/ViewManager.cs
  17. 159
      src/Avalonia.Controls/Repeater/ViewportManager.cs
  18. 1
      src/Avalonia.Controls/Repeater/VirtualizationInfo.cs
  19. 115
      src/Avalonia.Controls/ScrollViewer.cs
  20. 1
      src/Avalonia.Controls/TreeViewItem.cs
  21. 2
      src/Avalonia.Layout/AttachedLayout.cs
  22. 4
      src/Avalonia.Layout/ElementManager.cs
  23. 65
      src/Avalonia.Layout/FlowLayoutAlgorithm.cs
  24. 2
      src/Avalonia.Layout/LayoutHelper.cs
  25. 10
      src/Avalonia.Layout/LayoutManager.cs
  26. 2
      src/Avalonia.Layout/LayoutQueue.cs
  27. 4
      src/Avalonia.Layout/NonVirtualizingStackLayout.cs
  28. 8
      src/Avalonia.Layout/StackLayout.cs
  29. 7
      src/Avalonia.Layout/UniformGridLayout.cs
  30. 4
      src/Avalonia.Themes.Default/ToggleSwitch.xaml
  31. 7
      src/Avalonia.Themes.Fluent/Accents/BaseDark.xaml
  32. 7
      src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml
  33. 52
      src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml
  34. 51
      src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml
  35. 93
      src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml
  36. 97
      src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml
  37. 374
      src/Avalonia.Themes.Fluent/ScrollBar.xaml
  38. 116
      src/Avalonia.Themes.Fluent/ScrollViewer.xaml
  39. 4
      src/Avalonia.Themes.Fluent/ToggleSwitch.xaml
  40. 17
      src/Avalonia.Themes.Fluent/TreeView.xaml
  41. 246
      src/Avalonia.Themes.Fluent/TreeViewItem.xaml
  42. 19
      tests/Avalonia.Base.UnitTests/Utilities/MathUtilitiesTests.cs
  43. 8
      tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs
  44. 6
      tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests_ILogicalScrollable.cs
  45. 27
      tests/Avalonia.Layout.UnitTests/LayoutHelperTests.cs

4
readme.md

@ -31,7 +31,9 @@ Install-Package Avalonia.Desktop
Examples of UIs built with Avalonia
![image](https://user-images.githubusercontent.com/4672627/84707589-5b69a880-af35-11ea-87a6-7ad57a31d314.png)
![image](https://user-images.githubusercontent.com/4672627/84708576-28281900-af37-11ea-8c88-e29dfcfa0558.png)
![image](https://user-images.githubusercontent.com/4672627/85069644-d8419000-b18a-11ea-8732-be9055bb61fd.PNG)
![image](https://user-images.githubusercontent.com/4672627/85069659-dc6dad80-b18a-11ea-8375-39ef95315b5c.PNG)
![image](https://user-images.githubusercontent.com/4672627/84708947-c3b98980-af37-11ea-8c9d-503334615bbf.png)

1
samples/ControlCatalog/MainView.xaml

@ -55,6 +55,7 @@
<TabItem Header="Pointers (Touch)"><pages:PointersPage/></TabItem>
<TabItem Header="ProgressBar"><pages:ProgressBarPage/></TabItem>
<TabItem Header="RadioButton"><pages:RadioButtonPage/></TabItem>
<TabItem Header="ScrollViewer"><pages:ScrollViewerPage/></TabItem>
<TabItem Header="Slider"><pages:SliderPage/></TabItem>
<TabItem Header="TabControl"><pages:TabControlPage/></TabItem>
<TabItem Header="TabStrip"><pages:TabStripPage/></TabItem>

7
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml

@ -16,6 +16,8 @@
<Button Command="{Binding AddItem}">Add Item</Button>
<Button Command="{Binding RandomizeHeights}">Randomize Heights</Button>
<Button Command="{Binding ResetItems}">Reset items</Button>
<Button x:Name="scrollToLast">Scroll to Last</Button>
<Button x:Name="scrollToRandom">Scroll to Random</Button>
</StackPanel>
<Border BorderThickness="1" BorderBrush="{DynamicResource ThemeBorderMidBrush}" Margin="0 0 0 16">
<ScrollViewer Name="scroller"
@ -24,7 +26,10 @@
<ItemsRepeater Name="repeater" Background="Transparent" Items="{Binding Items}">
<ItemsRepeater.ItemTemplate>
<DataTemplate>
<TextBlock Focusable="True" Height="{Binding Height}" Text="{Binding Text}"/>
<TextBlock Focusable="True"
Background="{Binding Background}"
Height="{Binding Height}"
Text="{Binding Text}"/>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>

34
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs

@ -5,23 +5,32 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Markup.Xaml;
using Avalonia.VisualTree;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public class ItemsRepeaterPage : UserControl
{
private readonly ItemsRepeaterPageViewModel _viewModel;
private ItemsRepeater _repeater;
private ScrollViewer _scroller;
private Button _scrollToLast;
private Button _scrollToRandom;
private Random _random = new Random(0);
public ItemsRepeaterPage()
{
this.InitializeComponent();
_repeater = this.FindControl<ItemsRepeater>("repeater");
_scroller = this.FindControl<ScrollViewer>("scroller");
_scrollToLast = this.FindControl<Button>("scrollToLast");
_scrollToRandom = this.FindControl<Button>("scrollToRandom");
_repeater.PointerPressed += RepeaterClick;
_repeater.KeyDown += RepeaterOnKeyDown;
DataContext = new ItemsRepeaterPageViewModel();
_scrollToLast.Click += scrollToLast_Click;
_scrollToRandom.Click += scrollToRandom_Click;
DataContext = _viewModel = new ItemsRepeaterPageViewModel();
}
private void InitializeComponent()
@ -73,18 +82,37 @@ namespace ControlCatalog.Pages
}
}
private void ScrollTo(int index)
{
System.Diagnostics.Debug.WriteLine("Scroll to " + index);
var layoutManager = ((Window)this.GetVisualRoot()).LayoutManager;
var element = _repeater.GetOrCreateElement(index);
layoutManager.ExecuteLayoutPass();
element.BringIntoView();
}
private void RepeaterClick(object sender, PointerPressedEventArgs e)
{
var item = (e.Source as TextBlock)?.DataContext as ItemsRepeaterPageViewModel.Item;
((ItemsRepeaterPageViewModel)DataContext).SelectedItem = item;
_viewModel.SelectedItem = item;
}
private void RepeaterOnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.F5)
{
((ItemsRepeaterPageViewModel)DataContext).ResetItems();
_viewModel.ResetItems();
}
}
private void scrollToLast_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
{
ScrollTo(_viewModel.Items.Count - 1);
}
private void scrollToRandom_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
{
ScrollTo(_random.Next(_viewModel.Items.Count - 1));
}
}
}

35
samples/ControlCatalog/Pages/ScrollViewerPage.xaml

@ -0,0 +1,35 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ScrollViewerPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ScrollViewer</TextBlock>
<TextBlock Classes="h2">Allows for horizontal and vertical content scrolling.</TextBlock>
<Grid ColumnDefinitions="Auto, *">
<StackPanel Orientation="Vertical" Spacing="4">
<ToggleSwitch IsChecked="{Binding AllowAutoHide}" Content="Allow auto hide" />
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Text="Horizontal Scroll" />
<ComboBox Items="{Binding AvailableVisibility}" SelectedItem="{Binding HorizontalScrollVisibility}" />
</StackPanel>
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Text="Vertical Scroll" />
<ComboBox Items="{Binding AvailableVisibility}" SelectedItem="{Binding VerticalScrollVisibility}" />
</StackPanel>
</StackPanel>
<ScrollViewer x:Name="ScrollViewer"
Grid.Column="1"
Width="400" Height="400"
AllowAutoHide="{Binding AllowAutoHide}"
HorizontalScrollBarVisibility="{Binding HorizontalScrollVisibility}"
VerticalScrollBarVisibility="{Binding VerticalScrollVisibility}">
<Image Width="800" Height="800" Stretch="UniformToFill"
Source="/Assets/delicate-arch-896885_640.jpg" />
</ScrollViewer>
</Grid>
</StackPanel>
</UserControl>

65
samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs

@ -0,0 +1,65 @@
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Markup.Xaml;
using ReactiveUI;
namespace ControlCatalog.Pages
{
public class ScrollViewerPageViewModel : ReactiveObject
{
private bool _allowAutoHide;
private ScrollBarVisibility _horizontalScrollVisibility;
private ScrollBarVisibility _verticalScrollVisibility;
public ScrollViewerPageViewModel()
{
AvailableVisibility = new List<ScrollBarVisibility>
{
ScrollBarVisibility.Auto,
ScrollBarVisibility.Visible,
ScrollBarVisibility.Hidden,
ScrollBarVisibility.Disabled,
};
HorizontalScrollVisibility = ScrollBarVisibility.Auto;
VerticalScrollVisibility = ScrollBarVisibility.Auto;
AllowAutoHide = true;
}
public bool AllowAutoHide
{
get => _allowAutoHide;
set => this.RaiseAndSetIfChanged(ref _allowAutoHide, value);
}
public ScrollBarVisibility HorizontalScrollVisibility
{
get => _horizontalScrollVisibility;
set => this.RaiseAndSetIfChanged(ref _horizontalScrollVisibility, value);
}
public ScrollBarVisibility VerticalScrollVisibility
{
get => _verticalScrollVisibility;
set => this.RaiseAndSetIfChanged(ref _verticalScrollVisibility, value);
}
public List<ScrollBarVisibility> AvailableVisibility { get; }
}
public class ScrollViewerPage : UserControl
{
public ScrollViewerPage()
{
InitializeComponent();
DataContext = new ScrollViewerPageViewModel();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

13
samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using Avalonia.Media;
using ReactiveUI;
namespace ControlCatalog.ViewModels
@ -27,7 +28,7 @@ namespace ControlCatalog.ViewModels
public void AddItem()
{
var index = SelectedItem != null ? Items.IndexOf(SelectedItem) : -1;
Items.Insert(index + 1, new Item { Text = $"New Item {_newItemIndex++}" });
Items.Insert(index + 1, new Item(index + 1) { Text = $"New Item {_newItemIndex++}" });
}
public void RandomizeHeights()
@ -52,7 +53,7 @@ namespace ControlCatalog.ViewModels
_newGenerationIndex++;
return new ObservableCollection<Item>(
Enumerable.Range(1, 100000).Select(i => new Item
Enumerable.Range(1, 100000).Select(i => new Item(i)
{
Text = $"Item {i.ToString()} {suffix}"
}));
@ -61,6 +62,12 @@ namespace ControlCatalog.ViewModels
public class Item : ReactiveObject
{
private double _height = double.NaN;
private int _index;
public Item(int index)
{
_index = index;
}
public string Text { get; set; }
@ -69,6 +76,8 @@ namespace ControlCatalog.ViewModels
get => _height;
set => this.RaiseAndSetIfChanged(ref _height, value);
}
public IBrush Background => ((_index % 2) == 0) ? Brushes.Yellow : Brushes.Wheat;
}
}
}

33
src/Avalonia.Base/Utilities/MathUtilities.cs

@ -206,39 +206,6 @@ namespace Avalonia.Utilities
}
}
/// <summary>
/// Calculates the value to be used for layout rounding at high DPI.
/// </summary>
/// <param name="value">Input value to be rounded.</param>
/// <param name="dpiScale">Ratio of screen's DPI to layout DPI</param>
/// <returns>Adjusted value that will produce layout rounding on screen at high dpi.</returns>
/// <remarks>This is a layout helper method. It takes DPI into account and also does not return
/// the rounded value if it is unacceptable for layout, e.g. Infinity or NaN. It's a helper associated with
/// UseLayoutRounding property and should not be used as a general rounding utility.</remarks>
public static double RoundLayoutValue(double value, double dpiScale)
{
double newValue;
// If DPI == 1, don't use DPI-aware rounding.
if (!MathUtilities.IsOne(dpiScale))
{
newValue = Math.Round(value * dpiScale) / dpiScale;
// If rounding produces a value unacceptable to layout (NaN, Infinity or MaxValue), use the original value.
if (double.IsNaN(newValue) ||
double.IsInfinity(newValue) ||
MathUtilities.AreClose(newValue, double.MaxValue))
{
newValue = value;
}
}
else
{
newValue = Math.Round(value);
}
return newValue;
}
/// <summary>
/// Clamps a value between a minimum and maximum value.
/// </summary>

6
src/Avalonia.Controls/Grid.cs

@ -8,10 +8,8 @@ using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Avalonia;
using Avalonia.Collections;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Utilities;
using Avalonia.VisualTree;
@ -2103,7 +2101,7 @@ namespace Avalonia.Controls
for (int i = 0; i < definitions.Count; ++i)
{
DefinitionBase def = definitions[i];
double roundedSize = MathUtilities.RoundLayoutValue(def.SizeCache, dpi);
double roundedSize = LayoutHelper.RoundLayoutValue(def.SizeCache, dpi);
roundingErrors[i] = (roundedSize - def.SizeCache);
def.SizeCache = roundedSize;
roundedTakenSize += roundedSize;

20
src/Avalonia.Controls/IScrollAnchorProvider.cs

@ -1,9 +1,29 @@
namespace Avalonia.Controls
{
/// <summary>
/// Specifies a contract for a scrolling control that supports scroll anchoring.
/// </summary>
public interface IScrollAnchorProvider
{
/// <summary>
/// The currently chosen anchor element to use for scroll anchoring.
/// </summary>
IControl CurrentAnchor { get; }
/// <summary>
/// Registers a control as a potential scroll anchor candidate.
/// </summary>
/// <param name="element">
/// A control within the subtree of the <see cref="IScrollAnchorProvider"/>.
/// </param>
void RegisterAnchorCandidate(IControl element);
/// <summary>
/// Unregisters a control as a potential scroll anchor candidate.
/// </summary>
/// <param name="element">
/// A control within the subtree of the <see cref="IScrollAnchorProvider"/>.
/// </param>
void UnregisterAnchorCandidate(IControl element);
}
}

198
src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs

@ -3,10 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Runtime.InteropServices.ComTypes;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.VisualTree;
namespace Avalonia.Controls.Presenters
@ -14,7 +12,7 @@ namespace Avalonia.Controls.Presenters
/// <summary>
/// Presents a scrolling view of content inside a <see cref="ScrollViewer"/>.
/// </summary>
public class ScrollContentPresenter : ContentPresenter, IPresenter, IScrollable
public class ScrollContentPresenter : ContentPresenter, IPresenter, IScrollable, IScrollAnchorProvider
{
/// <summary>
/// Defines the <see cref="CanHorizontallyScroll"/> property.
@ -58,13 +56,19 @@ namespace Avalonia.Controls.Presenters
o => o.Viewport,
(o, v) => o.Viewport = v);
// Arbitrary chosen value, probably need to ask ILogicalScrollable
private const int LogicalScrollItemSize = 50;
private bool _canHorizontallyScroll;
private bool _canVerticallyScroll;
private bool _arranging;
private Size _extent;
private Vector _offset;
private IDisposable _logicalScrollSubscription;
private Size _viewport;
private Dictionary<int, Vector> _activeLogicalGestureScrolls;
private List<IControl> _anchorCandidates;
private (IControl control, Rect bounds) _anchor;
/// <summary>
/// Initializes static members of the <see cref="ScrollContentPresenter"/> class.
@ -73,7 +77,6 @@ namespace Avalonia.Controls.Presenters
{
ClipToBoundsProperty.OverrideDefaultValue(typeof(ScrollContentPresenter), true);
ChildProperty.Changed.AddClassHandler<ScrollContentPresenter>((x, e) => x.ChildChanged(e));
AffectsArrange<ScrollContentPresenter>(OffsetProperty);
}
/// <summary>
@ -87,6 +90,8 @@ namespace Avalonia.Controls.Presenters
this.GetObservable(ChildProperty).Subscribe(UpdateScrollableSubscription);
}
internal event EventHandler<VectorEventArgs> PreArrange;
/// <summary>
/// Gets or sets a value indicating whether the content can be scrolled horizontally.
/// </summary>
@ -120,7 +125,7 @@ namespace Avalonia.Controls.Presenters
public Vector Offset
{
get { return _offset; }
set { SetAndRaise(OffsetProperty, ref _offset, value); }
set { SetAndRaise(OffsetProperty, ref _offset, ScrollViewer.CoerceOffset(Extent, Viewport, value)); }
}
/// <summary>
@ -132,6 +137,9 @@ namespace Avalonia.Controls.Presenters
private set { SetAndRaise(ViewportProperty, ref _viewport, value); }
}
/// <inheritdoc/>
IControl IScrollAnchorProvider.CurrentAnchor => _anchor.control;
/// <summary>
/// Attempts to bring a portion of the target visual into view by scrolling the content.
/// </summary>
@ -196,6 +204,30 @@ namespace Avalonia.Controls.Presenters
return result;
}
/// <inheritdoc/>
void IScrollAnchorProvider.RegisterAnchorCandidate(IControl element)
{
if (!this.IsVisualAncestorOf(element))
{
throw new InvalidOperationException(
"An anchor control must be a visual descendent of the ScrollContentPresenter.");
}
_anchorCandidates ??= new List<IControl>();
_anchorCandidates.Add(element);
}
/// <inheritdoc/>
void IScrollAnchorProvider.UnregisterAnchorCandidate(IControl element)
{
_anchorCandidates?.Remove(element);
if (_anchor.control == element)
{
_anchor = default;
}
}
/// <inheritdoc/>
protected override Size MeasureOverride(Size availableSize)
{
@ -215,22 +247,85 @@ namespace Avalonia.Controls.Presenters
/// <inheritdoc/>
protected override Size ArrangeOverride(Size finalSize)
{
PreArrange?.Invoke(this, new VectorEventArgs
{
Vector = new Vector(finalSize.Width, finalSize.Height),
});
if (_logicalScrollSubscription != null || Child == null)
{
return base.ArrangeOverride(finalSize);
}
try
{
_arranging = true;
return ArrangeWithAnchoring(finalSize);
}
finally
{
_arranging = false;
}
}
private Size ArrangeWithAnchoring(Size finalSize)
{
var size = new Size(
CanHorizontallyScroll ? Math.Max(Child.DesiredSize.Width, finalSize.Width) : finalSize.Width,
CanVerticallyScroll ? Math.Max(Child.DesiredSize.Height, finalSize.Height) : finalSize.Height);
Vector TrackAnchor()
{
// If we have an anchor and its position relative to Child has changed during the
// arrange then that change wasn't just due to scrolling (as scrolling doesn't adjust
// relative positions within Child).
if (_anchor.control != null &&
TranslateBounds(_anchor.control, Child, out var updatedBounds) &&
updatedBounds.Position != _anchor.bounds.Position)
{
var offset = updatedBounds.Position - _anchor.bounds.Position;
return offset;
}
return default;
}
// Calculate the new anchor element.
_anchor = CalculateCurrentAnchor();
// Do the arrange.
ArrangeOverrideImpl(size, -Offset);
// If the anchor moved during the arrange, we need to adjust the offset and do another arrange.
var anchorShift = TrackAnchor();
if (anchorShift != default)
{
var newOffset = Offset + anchorShift;
var newExtent = Extent;
var maxOffset = new Vector(Extent.Width - Viewport.Width, Extent.Height - Viewport.Height);
if (newOffset.X > maxOffset.X)
{
newExtent = newExtent.WithWidth(newOffset.X + Viewport.Width);
}
if (newOffset.Y > maxOffset.Y)
{
newExtent = newExtent.WithHeight(newOffset.Y + Viewport.Height);
}
Extent = newExtent;
Offset = newOffset;
ArrangeOverrideImpl(size, -Offset);
}
Viewport = finalSize;
Extent = Child.Bounds.Size.Inflate(Child.Margin);
return finalSize;
}
// Arbitrary chosen value, probably need to ask ILogicalScrollable
private const int LogicalScrollItemSize = 50;
private void OnScrollGesture(object sender, ScrollGestureEventArgs e)
{
if (Extent.Height > Viewport.Height || Extent.Width > Viewport.Width)
@ -327,6 +422,16 @@ namespace Avalonia.Controls.Presenters
}
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
if (change.Property == OffsetProperty && !_arranging)
{
InvalidateArrange();
}
base.OnPropertyChanged(change);
}
private void BringIntoViewRequested(object sender, RequestBringIntoViewEventArgs e)
{
e.Handled = BringDescendantIntoView(e.TargetObject, e.TargetRect);
@ -390,5 +495,84 @@ namespace Avalonia.Controls.Presenters
Offset = scrollable.Offset;
}
}
private (IControl, Rect) CalculateCurrentAnchor()
{
if (_anchorCandidates == null)
{
return default;
}
var bestCandidate = default(IControl);
var bestCandidateDistance = double.MaxValue;
// Find the anchor candidate that is scrolled closest to the top-left of this
// ScrollContentPresenter.
foreach (var element in _anchorCandidates)
{
if (element.IsVisible && GetViewportBounds(element, out var bounds))
{
var distance = (Vector)bounds.Position;
var candidateDistance = Math.Abs(distance.Length);
if (candidateDistance < bestCandidateDistance)
{
bestCandidate = element;
bestCandidateDistance = candidateDistance;
}
}
}
if (bestCandidate != null)
{
// We have a candidate, calculate its bounds relative to Child. Because these
// bounds aren't relative to the ScrollContentPresenter itself, if they change
// then we know it wasn't just due to scrolling.
var unscrolledBounds = TranslateBounds(bestCandidate, Child);
return (bestCandidate, unscrolledBounds);
}
return default;
}
private bool GetViewportBounds(IControl element, out Rect bounds)
{
if (TranslateBounds(element, Child, out var childBounds))
{
// We want the bounds relative to the new Offset, regardless of whether the child
// control has actually been arranged to this offset yet, so translate first to the
// child control and then apply Offset rather than translating directly to this
// control.
var thisBounds = new Rect(Bounds.Size);
bounds = new Rect(childBounds.Position - Offset, childBounds.Size);
return bounds.Intersects(thisBounds);
}
bounds = default;
return false;
}
private Rect TranslateBounds(IControl control, IControl to)
{
if (TranslateBounds(control, to, out var bounds))
{
return bounds;
}
throw new InvalidOperationException("The control's bounds could not be translated to the requested control.");
}
private bool TranslateBounds(IControl control, IControl to, out Rect bounds)
{
if (!control.IsVisible)
{
bounds = default;
return false;
}
var p = control.TranslatePoint(default, to);
bounds = p.HasValue ? new Rect(p.Value, control.Bounds.Size) : default;
return p.HasValue;
}
}
}

27
src/Avalonia.Controls/Primitives/HeaderedContentControl.cs

@ -1,8 +1,9 @@
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.LogicalTree;
#nullable enable
namespace Avalonia.Controls.Primitives
{
/// <summary>
@ -13,36 +14,36 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// Defines the <see cref="Header"/> property.
/// </summary>
public static readonly StyledProperty<object> HeaderProperty =
AvaloniaProperty.Register<HeaderedContentControl, object>(nameof(Header));
public static readonly StyledProperty<object?> HeaderProperty =
AvaloniaProperty.Register<HeaderedContentControl, object?>(nameof(Header));
/// <summary>
/// Defines the <see cref="HeaderTemplate"/> property.
/// </summary>
public static readonly StyledProperty<IDataTemplate> HeaderTemplateProperty =
AvaloniaProperty.Register<HeaderedContentControl, IDataTemplate>(nameof(HeaderTemplate));
public static readonly StyledProperty<IDataTemplate?> HeaderTemplateProperty =
AvaloniaProperty.Register<HeaderedContentControl, IDataTemplate?>(nameof(HeaderTemplate));
/// <summary>
/// Initializes static members of the <see cref="ContentControl"/> class.
/// </summary>
static HeaderedContentControl()
{
ContentProperty.Changed.AddClassHandler<HeaderedContentControl>((x, e) => x.HeaderChanged(e));
HeaderProperty.Changed.AddClassHandler<HeaderedContentControl>((x, e) => x.HeaderChanged(e));
}
/// <summary>
/// Gets or sets the header content.
/// </summary>
public object Header
public object? Header
{
get { return GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
get => GetValue(HeaderProperty);
set => SetValue(HeaderProperty, value);
}
/// <summary>
/// Gets the header presenter from the control's template.
/// </summary>
public IContentPresenter HeaderPresenter
public IContentPresenter? HeaderPresenter
{
get;
private set;
@ -51,10 +52,10 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// Gets or sets the data template used to display the header content of the control.
/// </summary>
public IDataTemplate HeaderTemplate
public IDataTemplate? HeaderTemplate
{
get { return GetValue(HeaderTemplateProperty); }
set { SetValue(HeaderTemplateProperty, value); }
get => GetValue(HeaderTemplateProperty);
set => SetValue(HeaderTemplateProperty, value);
}
/// <inheritdoc/>

123
src/Avalonia.Controls/Primitives/ScrollBar.cs

@ -3,6 +3,7 @@ using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Threading;
namespace Avalonia.Controls.Primitives
{
@ -40,10 +41,26 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<Orientation> OrientationProperty =
AvaloniaProperty.Register<ScrollBar, Orientation>(nameof(Orientation), Orientation.Vertical);
/// <summary>
/// Defines the <see cref="IsExpandedProperty"/> property.
/// </summary>
public static readonly DirectProperty<ScrollBar, bool> IsExpandedProperty =
AvaloniaProperty.RegisterDirect<ScrollBar, bool>(
nameof(IsExpanded),
o => o.IsExpanded);
/// <summary>
/// Defines the <see cref="AllowAutoHide"/> property.
/// </summary>
public static readonly StyledProperty<bool> AllowAutoHideProperty =
AvaloniaProperty.Register<ScrollBar, bool>(nameof(AllowAutoHide), true);
private Button _lineUpButton;
private Button _lineDownButton;
private Button _pageUpButton;
private Button _pageDownButton;
private DispatcherTimer _timer;
private bool _isExpanded;
/// <summary>
/// Initializes static members of the <see cref="ScrollBar"/> class.
@ -90,6 +107,24 @@ namespace Avalonia.Controls.Primitives
set { SetValue(OrientationProperty, value); }
}
/// <summary>
/// Gets a value that indicates whether the scrollbar is expanded.
/// </summary>
public bool IsExpanded
{
get => _isExpanded;
private set => SetAndRaise(IsExpandedProperty, ref _isExpanded, value);
}
/// <summary>
/// Gets a value that indicates whether the scrollbar can hide itself when user is not interacting with it.
/// </summary>
public bool AllowAutoHide
{
get => GetValue(AllowAutoHideProperty);
set => SetValue(AllowAutoHideProperty, value);
}
public event EventHandler<ScrollEventArgs> Scroll;
/// <summary>
@ -131,6 +166,10 @@ namespace Avalonia.Controls.Primitives
{
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<Orientation>());
}
else if (change.Property == AllowAutoHideProperty)
{
UpdateIsExpandedState();
}
else
{
if (change.Property == MinimumProperty ||
@ -143,6 +182,26 @@ namespace Avalonia.Controls.Primitives
}
}
protected override void OnPointerEnter(PointerEventArgs e)
{
base.OnPointerEnter(e);
if (AllowAutoHide)
{
ExpandAfterDelay();
}
}
protected override void OnPointerLeave(PointerEventArgs e)
{
base.OnPointerLeave(e);
if (AllowAutoHide)
{
CollapseAfterDelay();
}
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
if (_lineUpButton != null)
@ -170,8 +229,6 @@ namespace Avalonia.Controls.Primitives
_pageUpButton = e.NameScope.Find<Button>("PART_PageUpButton");
_pageDownButton = e.NameScope.Find<Button>("PART_PageDownButton");
if (_lineUpButton != null)
{
_lineUpButton.Click += LineUpClick;
@ -193,6 +250,68 @@ namespace Avalonia.Controls.Primitives
}
}
private void InvokeAfterDelay(Action handler, TimeSpan delay)
{
if (_timer != null)
{
_timer.Stop();
}
else
{
_timer = new DispatcherTimer(DispatcherPriority.Normal);
_timer.Tick += (sender, args) =>
{
var senderTimer = (DispatcherTimer)sender;
if (senderTimer.Tag is Action action)
{
action();
}
senderTimer.Stop();
};
}
_timer.Tag = handler;
_timer.Interval = delay;
_timer.Start();
}
private void UpdateIsExpandedState()
{
if (!AllowAutoHide)
{
_timer?.Stop();
IsExpanded = true;
}
else
{
IsExpanded = IsPointerOver;
}
}
private void CollapseAfterDelay()
{
InvokeAfterDelay(Collapse, TimeSpan.FromSeconds(2));
}
private void ExpandAfterDelay()
{
InvokeAfterDelay(Expand, TimeSpan.FromMilliseconds(400));
}
private void Collapse()
{
IsExpanded = false;
}
private void Expand()
{
IsExpanded = true;
}
private void LineUpClick(object sender, RoutedEventArgs e)
{
SmallDecrement();

8
src/Avalonia.Controls/Repeater/ItemsRepeater.cs

@ -10,6 +10,7 @@ using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Logging;
using Avalonia.VisualTree;
namespace Avalonia.Controls
@ -80,6 +81,7 @@ namespace Avalonia.Controls
static ItemsRepeater()
{
ClipToBoundsProperty.OverrideDefaultValue<ItemsRepeater>(true);
RequestBringIntoViewEvent.AddClassHandler<ItemsRepeater>((x, e) => x.OnRequestBringIntoView(e));
}
/// <summary>
@ -305,6 +307,7 @@ namespace Avalonia.Controls
virtInfo.AutoRecycleCandidate &&
!virtInfo.KeepAlive)
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "AutoClear - {Index}", virtInfo.Index);
ClearElementImpl(element);
}
}
@ -743,6 +746,11 @@ namespace Avalonia.Controls
}
}
private void OnRequestBringIntoView(RequestBringIntoViewEventArgs e)
{
_viewportManager.OnBringIntoViewRequested(e);
}
private void InvalidateMeasureForLayout(object sender, EventArgs e) => InvalidateMeasure();
private void InvalidateArrangeForLayout(object sender, EventArgs e) => InvalidateArrange();

7
src/Avalonia.Controls/Repeater/RepeaterLayoutContext.cs

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Layout;
using Avalonia.Logging;
namespace Avalonia.Controls
{
@ -58,7 +59,11 @@ namespace Avalonia.Controls
protected override object GetItemAtCore(int index) => _owner.ItemsSourceView.GetAt(index);
protected override void RecycleElementCore(ILayoutable element) => _owner.ClearElementImpl((IControl)element);
protected override void RecycleElementCore(ILayoutable element)
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "RepeaterLayout - RecycleElement: {Index}", _owner.GetElementIndex((IControl)element));
_owner.ClearElementImpl((IControl)element);
}
protected override Rect RealizationRectCore() => _owner.RealizationWindow;
}

27
src/Avalonia.Controls/Repeater/ViewManager.cs

@ -11,6 +11,7 @@ using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Logging;
using Avalonia.VisualTree;
namespace Avalonia.Controls
@ -60,11 +61,13 @@ namespace Avalonia.Controls
if (suppressAutoRecycle)
{
virtInfo.AutoRecycleCandidate = false;
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "GetElement: {Index} Not AutoRecycleCandidate:", virtInfo.Index);
}
else
{
virtInfo.AutoRecycleCandidate = true;
virtInfo.KeepAlive = true;
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "GetElement: {Index} AutoRecycleCandidate:", virtInfo.Index);
}
return element;
@ -107,10 +110,28 @@ namespace Avalonia.Controls
}
}
// We need to clear the datacontext to prevent crashes from happening,
// however we only do that if we were the ones setting it.
// That is when one of the following is the case (numbering taken from line ~642):
// 1.2 No ItemTemplate, data is not a UIElement
// 2.1 ItemTemplate, data is not FrameworkElement
// 2.2.2 Itemtemplate, data is FrameworkElement, ElementFactory returned Element different to data
//
// In all of those three cases, we the ItemTemplateShim is NOT null.
// Luckily when we create the items, we store whether we were the once setting the DataContext.
public void ClearElementToElementFactory(IControl element)
{
_owner.OnElementClearing(element);
var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
virtInfo.MoveOwnershipToElementFactory();
// During creation of this object, we were the one setting the DataContext, so clear it now.
if (virtInfo.MustClearDataContext)
{
element.DataContext = null;
}
if (_owner.ItemTemplateShim != null)
{
_owner.ItemTemplateShim.RecycleElement(_owner, element);
@ -124,9 +145,6 @@ namespace Avalonia.Controls
}
}
var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
virtInfo.MoveOwnershipToElementFactory();
if (_lastFocusedElement == element)
{
// Focused element is going away. Remove the tracked last focused element
@ -594,11 +612,14 @@ namespace Avalonia.Controls
{
virtInfo = ItemsRepeater.CreateAndInitializeVirtualizationInfo(element);
}
// Clear flag
virtInfo.MustClearDataContext = false;
if (data != element)
{
// Prepare the element
element.DataContext = data;
virtInfo.MustClearDataContext = true;
}
virtInfo.MoveOwnershipToLayoutFromElementFactory(

159
src/Avalonia.Controls/Repeater/ViewportManager.cs

@ -5,9 +5,15 @@
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Controls.Presenters;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Logging;
using Avalonia.Media;
using Avalonia.Reactive;
using Avalonia.Threading;
using Avalonia.VisualTree;
@ -35,8 +41,8 @@ namespace Avalonia.Controls
// actually happened. This can happen in cases where no scrollviewer
// in the parent chain can scroll in the shift direction.
private Point _unshiftableShift;
private double _maximumHorizontalCacheLength = 0.0;
private double _maximumVerticalCacheLength = 0.0;
private double _maximumHorizontalCacheLength = 2.0;
private double _maximumVerticalCacheLength = 2.0;
private double _horizontalCacheBufferPerSide;
private double _verticalCacheBufferPerSide;
private bool _isBringIntoViewInProgress;
@ -184,6 +190,9 @@ namespace Avalonia.Controls
// We tolerate viewport imprecisions up to 1 pixel to avoid invaliding layout too much.
if (Math.Abs(_expectedViewportShift.X) > 1 || Math.Abs(_expectedViewportShift.Y) > 1)
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Expecting viewport shift of ({Shift})",
_owner.Layout.LayoutId, _expectedViewportShift);
// There are cases where we might be expecting a shift but not get it. We will
// be waiting for the effective viewport event but if the scroll viewer is not able
// to perform the shift (perhaps because it cannot scroll in negative offset),
@ -229,14 +238,12 @@ namespace Avalonia.Controls
public void OnElementPrepared(IControl element)
{
// If we have an anchor element, we do not want the
// scroll anchor provider to start anchoring some other element.
////element.CanBeScrollAnchor(true);
_scroller?.RegisterAnchorCandidate(element);
}
public void OnElementCleared(ILayoutable element)
public void OnElementCleared(IControl element)
{
////element.CanBeScrollAnchor(false);
_scroller?.UnregisterAnchorCandidate(element);
}
public void OnOwnerMeasuring()
@ -282,6 +289,7 @@ namespace Avalonia.Controls
private void OnLayoutUpdated(object sender, EventArgs args)
{
_owner.LayoutUpdated -= OnLayoutUpdated;
_layoutUpdatedSubscribed = false;
if (_managingViewportDisabled)
{
return;
@ -293,6 +301,10 @@ namespace Avalonia.Controls
// that can scroll in the direction where the shift is expected.
if (_pendingViewportShift.X != 0 || _pendingViewportShift.Y != 0)
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Layout Updated with pending shift {Shift}- invalidating measure",
_owner.Layout.LayoutId,
_pendingViewportShift);
// Assume this is never going to come.
_unshiftableShift = new Point(
_unshiftableShift.X + _pendingViewportShift.X,
@ -306,8 +318,11 @@ namespace Avalonia.Controls
public void OnMakeAnchor(IControl anchor, bool isAnchorOutsideRealizedRange)
{
_makeAnchorElement = anchor;
_isAnchorOutsideRealizedRange = isAnchorOutsideRealizedRange;
if (_makeAnchorElement != anchor)
{
_makeAnchorElement = anchor;
_isAnchorOutsideRealizedRange = isAnchorOutsideRealizedRange;
}
}
public void OnBringIntoViewRequested(RequestBringIntoViewEventArgs args)
@ -328,26 +343,28 @@ namespace Avalonia.Controls
// Make sure that only the target child can be the anchor during the bring into view operation.
foreach (var child in _owner.Children)
{
////if (child.CanBeScrollAnchor && child != targetChild)
////{
//// child.CanBeScrollAnchor = false;
////}
if (child != targetChild)
{
_scroller.UnregisterAnchorCandidate(child);
}
}
// Register to rendering event to go back to how things were before where any child can be the anchor.
_isBringIntoViewInProgress = true;
////if (!m_renderingToken)
////{
//// winrt::Windows::UI::Xaml::Media::CompositionTarget compositionTarget{ nullptr };
//// m_renderingToken = compositionTarget.Rendering(winrt::auto_revoke, { this, &ViewportManagerWithPlatformFeatures::OnCompositionTargetRendering });
////}
// Register action to go back to how things were before where any child can be the anchor. Here,
// WinUI uses CompositionTarget.Rendering but we don't currently have that, so post an action to
// run *after* rendering has completed (priority needs to be lower than Render as Transformed
// bounds must have been set in order for OnEffectiveViewportChanged to trigger).
if (!_isBringIntoViewInProgress)
{
_isBringIntoViewInProgress = true;
Dispatcher.UIThread.Post(OnCompositionTargetRendering, DispatcherPriority.Loaded);
}
}
}
private IControl GetImmediateChildOfRepeater(IControl descendant)
{
var targetChild = descendant;
var parent = descendant.Parent;
var parent = (IControl)descendant.VisualParent;
while (parent != null && parent != _owner)
{
targetChild = parent;
@ -362,26 +379,50 @@ namespace Avalonia.Controls
return targetChild;
}
public void ResetScrollers()
private void OnCompositionTargetRendering()
{
_scroller = null;
_effectiveViewportChangedRevoker?.Dispose();
_effectiveViewportChangedRevoker = null;
_ensuredScroller = false;
_isBringIntoViewInProgress = false;
_makeAnchorElement = null;
if (_scroller is object)
{
foreach (var child in _owner.Children)
{
var info = ItemsRepeater.GetVirtualizationInfo(child);
if (info.IsRealized && info.IsHeldByLayout)
{
_scroller.RegisterAnchorCandidate(child);
}
}
}
// HACK: Invalidate measure now that the anchor has been removed so that a layout can be
// done with a proper realization rect. This is a hack not present upstream to try to fix
// https://github.com/microsoft/microsoft-ui-xaml/issues/1422
TryInvalidateMeasure();
}
private void OnEffectiveViewportChanged(TransformedBounds? bounds)
public void ResetScrollers()
{
if (!bounds.HasValue)
if (_scroller is object)
{
return;
foreach (var child in _owner.Children)
{
_scroller.UnregisterAnchorCandidate(child);
}
_scroller = null;
}
var globalClip = bounds.Value.Clip;
var transform = _owner.GetVisualRoot().TransformToVisual(_owner).Value;
var clip = globalClip.TransformToAABB(transform);
var effectiveViewport = clip.Intersect(bounds.Value.Bounds);
_effectiveViewportChangedRevoker?.Dispose();
_effectiveViewportChangedRevoker = null;
_ensuredScroller = false;
}
private void OnEffectiveViewportChanged(Rect effectiveViewport)
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: EffectiveViewportChanged event callback", _owner.Layout.LayoutId);
UpdateViewport(effectiveViewport);
_pendingViewportShift = default;
@ -397,6 +438,7 @@ namespace Avalonia.Controls
if (_layoutUpdatedSubscribed)
{
_owner.LayoutUpdated -= OnLayoutUpdated;
_layoutUpdatedSubscribed = false;
}
}
@ -437,10 +479,17 @@ namespace Avalonia.Controls
private void UpdateViewport(Rect viewport)
{
var currentVisibleWindow = viewport;
var previousVisibleWindow = _visibleWindow;
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Effective Viewport: ({Before})->({After})",
_owner.Layout.LayoutId,
previousVisibleWindow,
viewport);
if (-currentVisibleWindow.X <= ItemsRepeater.ClearedElementsArrangePosition.X &&
-currentVisibleWindow.Y <= ItemsRepeater.ClearedElementsArrangePosition.Y)
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Viewport is invalid. visible window cleared", _owner.Layout.LayoutId);
// We got cleared.
_visibleWindow = default;
}
@ -449,7 +498,14 @@ namespace Avalonia.Controls
_visibleWindow = currentVisibleWindow;
}
TryInvalidateMeasure();
if (_visibleWindow != previousVisibleWindow)
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Used Viewport: ({Before})->({After})",
_owner.Layout.LayoutId,
previousVisibleWindow,
currentVisibleWindow);
TryInvalidateMeasure();
}
}
private static void ValidateCacheLength(double cacheLength)
@ -468,6 +524,7 @@ namespace Avalonia.Controls
// We invalidate measure instead of just invalidating arrange because
// we don't invalidate measure in UpdateViewport if the view is changing to
// avoid layout cycles.
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Invalidating measure due to viewport change", _owner.Layout.LayoutId);
_owner.InvalidateMeasure();
}
}
@ -476,16 +533,32 @@ namespace Avalonia.Controls
{
// HACK: This is a bit of a hack. We need the effective viewport of the ItemsRepeater -
// we can get this from TransformedBounds, but this property is updated after layout has
// run, resulting in the UI being updated too late when scrolling quickly. We can
// partially remedey this by triggering also on Bounds changes, but this won't work so
// well for nested ItemsRepeaters.
// run, which is too late. Instead, for now lets just hook into an internal event on
// ScrollContentPresenter to find out what the offset and viewport will be after arrange
// and use those values. Note that this doesn't handle nested ScrollViewers at all, but
// it's enough to get scrolling to non-uniformly sized items working for now.
//
// UWP uses the EffectiveBoundsChanged event (which I think was implemented specially
// for this case): we need to implement that in Avalonia.
return control.GetObservable(Visual.TransformedBoundsProperty)
.Merge(control.GetObservable(Visual.BoundsProperty).Select(_ => control.TransformedBounds))
.Skip(1)
.Subscribe(OnEffectiveViewportChanged);
// UWP uses the EffectiveViewportChanged event (which I think was implemented specially
// for this case): we need to implement that in Avalonia, but the semantics of it aren't
// clear to me. Hopefully the source for this event will be released with WinUI 3.
if (control.VisualParent is ScrollContentPresenter scp)
{
scp.PreArrange += ScrollContentPresenterPreArrange;
return Disposable.Create(() => scp.PreArrange -= ScrollContentPresenterPreArrange);
}
return Disposable.Empty;
}
private void ScrollContentPresenterPreArrange(object sender, VectorEventArgs e)
{
var scp = (ScrollContentPresenter)sender;
var effectiveViewport = new Rect((Point)scp.Offset, new Size(e.Vector.X, e.Vector.Y));
if (effectiveViewport != _visibleWindow)
{
OnEffectiveViewportChanged(effectiveViewport);
}
}
private class ScrollerInfo

1
src/Avalonia.Controls/Repeater/VirtualizationInfo.cs

@ -36,6 +36,7 @@ namespace Avalonia.Controls
public bool IsHeldByLayout => Owner == ElementOwner.Layout;
public bool IsRealized => IsHeldByLayout || Owner == ElementOwner.PinnedPool;
public bool IsInUniqueIdResetPool => Owner == ElementOwner.UniqueIdResetPool;
public bool MustClearDataContext { get; set; }
public bool KeepAlive { get; set; }
public ElementOwner Owner { get; private set; } = ElementOwner.ElementFactory;
public string UniqueId { get; private set; }

115
src/Avalonia.Controls/ScrollViewer.cs

@ -1,4 +1,5 @@
using System;
using System.Reactive.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
@ -166,6 +167,18 @@ namespace Avalonia.Controls
nameof(VerticalScrollBarVisibility),
ScrollBarVisibility.Auto);
/// <summary>
/// Defines the <see cref="IsExpandedProperty"/> property.
/// </summary>
public static readonly DirectProperty<ScrollViewer, bool> IsExpandedProperty =
ScrollBar.IsExpandedProperty.AddOwner<ScrollViewer>(o => o.IsExpanded);
/// <summary>
/// Defines the <see cref="AllowAutoHide"/> property.
/// </summary>
public static readonly StyledProperty<bool> AllowAutoHideProperty =
ScrollBar.AllowAutoHideProperty.AddOwner<ScrollViewer>();
/// <summary>
/// Defines the <see cref="ScrollChanged"/> event.
/// </summary>
@ -186,6 +199,8 @@ namespace Avalonia.Controls
private Size _oldViewport;
private Size _largeChange;
private Size _smallChange = new Size(DefaultSmallChange, DefaultSmallChange);
private bool _isExpanded;
private IDisposable _scrollBarExpandSubscription;
/// <summary>
/// Initializes static members of the <see cref="ScrollViewer"/> class.
@ -244,9 +259,7 @@ namespace Avalonia.Controls
set
{
value = ValidateOffset(this, value);
if (SetAndRaise(OffsetProperty, ref _offset, value))
if (SetAndRaise(OffsetProperty, ref _offset, CoerceOffset(Extent, Viewport, value)))
{
CalculatedPropertiesChanged();
}
@ -316,6 +329,9 @@ namespace Avalonia.Controls
get { return VerticalScrollBarVisibility != ScrollBarVisibility.Disabled; }
}
/// <inheritdoc/>
public IControl CurrentAnchor => (Presenter as IScrollAnchorProvider)?.CurrentAnchor;
/// <summary>
/// Gets the maximum horizontal scrollbar value.
/// </summary>
@ -382,8 +398,23 @@ namespace Avalonia.Controls
get { return _viewport.Height; }
}
/// <inheritdoc/>
IControl IScrollAnchorProvider.CurrentAnchor => null; // TODO: Implement
/// <summary>
/// Gets a value that indicates whether any scrollbar is expanded.
/// </summary>
public bool IsExpanded
{
get => _isExpanded;
private set => SetAndRaise(ScrollBar.IsExpandedProperty, ref _isExpanded, value);
}
/// <summary>
/// Gets a value that indicates whether scrollbars can hide itself when user is not interacting with it.
/// </summary>
public bool AllowAutoHide
{
get => GetValue(AllowAutoHideProperty);
set => SetValue(AllowAutoHideProperty, value);
}
/// <summary>
/// Scrolls the content up one line.
@ -473,14 +504,16 @@ namespace Avalonia.Controls
control.SetValue(VerticalScrollBarVisibilityProperty, value);
}
void IScrollAnchorProvider.RegisterAnchorCandidate(IControl element)
/// <inheritdoc/>
public void RegisterAnchorCandidate(IControl element)
{
// TODO: Implement
(Presenter as IScrollAnchorProvider)?.RegisterAnchorCandidate(element);
}
void IScrollAnchorProvider.UnregisterAnchorCandidate(IControl element)
/// <inheritdoc/>
public void UnregisterAnchorCandidate(IControl element)
{
// TODO: Implement
(Presenter as IScrollAnchorProvider)?.UnregisterAnchorCandidate(element);
}
protected override bool RegisterContentPresenter(IContentPresenter presenter)
@ -517,22 +550,6 @@ namespace Avalonia.Controls
return double.IsNaN(result) ? 0 : result;
}
private static Vector ValidateOffset(AvaloniaObject o, Vector value)
{
ScrollViewer scrollViewer = o as ScrollViewer;
if (scrollViewer != null)
{
var extent = scrollViewer.Extent;
var viewport = scrollViewer.Viewport;
return CoerceOffset(extent, viewport, value);
}
else
{
return value;
}
}
private void ChildChanged(IControl child)
{
if (_logicalScrollable is object)
@ -630,6 +647,54 @@ namespace Avalonia.Controls
RaiseEvent(e);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_scrollBarExpandSubscription?.Dispose();
_scrollBarExpandSubscription = SubscribeToScrollBars(e);
}
private IDisposable SubscribeToScrollBars(TemplateAppliedEventArgs e)
{
static IObservable<bool> GetExpandedObservable(ScrollBar scrollBar)
{
return scrollBar?.GetObservable(ScrollBar.IsExpandedProperty);
}
var horizontalScrollBar = e.NameScope.Find<ScrollBar>("PART_HorizontalScrollBar");
var verticalScrollBar = e.NameScope.Find<ScrollBar>("PART_VerticalScrollBar");
var horizontalExpanded = GetExpandedObservable(horizontalScrollBar);
var verticalExpanded = GetExpandedObservable(verticalScrollBar);
IObservable<bool> actualExpanded = null;
if (horizontalExpanded != null && verticalExpanded != null)
{
actualExpanded = horizontalExpanded.CombineLatest(verticalExpanded, (h, v) => h || v);
}
else
{
if (horizontalExpanded != null)
{
actualExpanded = horizontalExpanded;
}
else if (verticalExpanded != null)
{
actualExpanded = verticalExpanded;
}
}
return actualExpanded?.Subscribe(OnScrollBarExpandedChanged);
}
private void OnScrollBarExpandedChanged(bool isExpanded)
{
IsExpanded = isExpanded;
}
private void OnLayoutUpdated(object sender, EventArgs e) => RaiseScrollChanged();
private void RaiseScrollChanged()

1
src/Avalonia.Controls/TreeViewItem.cs

@ -49,6 +49,7 @@ namespace Avalonia.Controls
static TreeViewItem()
{
SelectableMixin.Attach<TreeViewItem>(IsSelectedProperty);
PressedMixin.Attach<TreeViewItem>();
FocusableProperty.OverrideDefaultValue<TreeViewItem>(true);
ItemsPanelProperty.OverrideDefaultValue<TreeViewItem>(DefaultPanel);
ParentProperty.Changed.AddClassHandler<TreeViewItem>((o, e) => o.OnParentChanged(e));

2
src/Avalonia.Layout/AttachedLayout.cs

@ -12,7 +12,7 @@ namespace Avalonia.Layout
/// </summary>
public abstract class AttachedLayout : AvaloniaObject
{
internal string LayoutId { get; set; }
public string LayoutId { get; set; }
/// <summary>
/// Occurs when the measurement state (layout) has been invalidated.

4
src/Avalonia.Layout/ElementManager.cs

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using Avalonia.Layout.Utils;
using Avalonia.Logging;
namespace Avalonia.Layout
{
@ -78,6 +79,7 @@ namespace Avalonia.Layout
{
// Sentinel. Create the element now since we need it.
int dataIndex = GetDataIndexFromRealizedRangeIndex(realizedIndex);
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "Creating element for sentinal with data index {Index}", dataIndex);
element = _context.GetOrCreateElementAt(
dataIndex,
ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
@ -232,6 +234,8 @@ namespace Avalonia.Layout
{
Insert(0, dataIndex, element);
}
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Created element for index {index}", layoutId, dataIndex);
}
}

65
src/Avalonia.Layout/FlowLayoutAlgorithm.cs

@ -5,6 +5,7 @@
using System;
using System.Collections.Specialized;
using Avalonia.Logging;
namespace Avalonia.Layout
{
@ -82,6 +83,9 @@ namespace Avalonia.Layout
// If minor size is infinity, there is only one line and no need to align that line.
_scrollOrientationSameAsFlow = double.IsInfinity(_orientation.Minor(availableSize));
var realizationRect = RealizationRect;
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: MeasureLayout Realization({Rect})",
layoutId,
realizationRect);
var suggestedAnchorIndex = _context.RecommendedAnchorIndex;
if (_elementManager.IsIndexValidInData(suggestedAnchorIndex))
@ -100,6 +104,7 @@ namespace Avalonia.Layout
Generate(GenerateDirection.Backward, anchorIndex, availableSize, minItemSpacing, lineSpacing, maxItemsPerLine, disableVirtualization, layoutId);
if (isWrapping && IsReflowRequired())
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Reflow Pass", layoutId);
var firstElementBounds = _elementManager.GetLayoutBoundsForRealizedIndex(0);
_orientation.SetMinorStart(ref firstElementBounds, 0);
_elementManager.SetLayoutBoundsForRealizedIndex(0, firstElementBounds);
@ -121,6 +126,7 @@ namespace Avalonia.Layout
LineAlignment lineAlignment,
string layoutId)
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: ArrangeLayout", layoutId);
ArrangeVirtualizingLayout(finalSize, lineAlignment, isWrapping, layoutId);
return new Size(
@ -184,6 +190,7 @@ namespace Avalonia.Layout
if (isAnchorSuggestionValid)
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Using suggested anchor {Anchor}", layoutId, suggestedAnchorIndex);
anchorIndex = _algorithmCallbacks.Algorithm_GetAnchorForTargetElement(
suggestedAnchorIndex,
availableSize,
@ -223,6 +230,9 @@ namespace Avalonia.Layout
}
else if (needAnchorColumnRevaluation || !isRealizationWindowConnected)
{
if (needAnchorColumnRevaluation) { Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: NeedAnchorColumnReevaluation", layoutId); }
if (!isRealizationWindowConnected) { Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Disconnected Window", layoutId); }
// The anchor is based on the realization window because a connected ItemsRepeater might intersect the realization window
// but not the visible window. In that situation, we still need to produce a valid anchor.
var anchorInfo = _algorithmCallbacks.Algorithm_GetAnchorForRealizationRect(availableSize, context);
@ -231,6 +241,7 @@ namespace Avalonia.Layout
}
else
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Connected Window - picking first realized element as anchor", layoutId);
// No suggestion - just pick first in realized range
anchorIndex = _elementManager.GetDataIndexFromRealizedRangeIndex(0);
var firstElementBounds = _elementManager.GetLayoutBoundsForRealizedIndex(0);
@ -238,12 +249,14 @@ namespace Avalonia.Layout
}
}
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Picked anchor: {Anchor}", layoutId, anchorIndex);
_firstRealizedDataIndexInsideRealizationWindow = _lastRealizedDataIndexInsideRealizationWindow = anchorIndex;
if (_elementManager.IsIndexValidInData(anchorIndex))
{
if (!_elementManager.IsDataIndexRealized(anchorIndex))
{
// Disconnected, throw everything and create new anchor
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId} Disconnected Window - throwing away all realized elements", layoutId);
_elementManager.ClearRealizedRange();
var anchor = _context.GetOrCreateElementAt(anchorIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
@ -254,9 +267,17 @@ namespace Avalonia.Layout
var desiredSize = MeasureElement(anchorElement, anchorIndex, availableSize, _context);
var layoutBounds = new Rect(anchorPosition.X, anchorPosition.Y, desiredSize.Width, desiredSize.Height);
_elementManager.SetLayoutBoundsForDataIndex(anchorIndex, layoutBounds);
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Layout bounds of anchor {anchor} are ({Bounds})",
layoutId,
anchorIndex,
layoutBounds);
}
else
{
// Throw everything away
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId} Anchor index is not valid - throwing away all realized elements",
layoutId);
_elementManager.ClearRealizedRange();
}
@ -280,6 +301,12 @@ namespace Avalonia.Layout
if (anchorIndex != -1)
{
int step = (direction == GenerateDirection.Forward) ? 1 : -1;
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Generating {Direction} from anchor {Anchor}",
layoutId,
direction,
anchorIndex);
int previousIndex = anchorIndex;
int currentIndex = anchorIndex + step;
var anchorBounds = _elementManager.GetLayoutBoundsForDataIndex(anchorIndex);
@ -365,6 +392,10 @@ namespace Avalonia.Layout
_orientation.SetMajorStart(ref bounds, previousLineOffset - lineMajorSize - lineSpacing);
_orientation.SetMajorSize(ref bounds, lineMajorSize);
_elementManager.SetLayoutBoundsForDataIndex(dataIndex, bounds);
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Corrected Layout bounds of element {Index} are ({Bounds})",
layoutId,
dataIndex,
bounds);
}
}
}
@ -387,6 +418,11 @@ namespace Avalonia.Layout
}
_elementManager.SetLayoutBoundsForDataIndex(currentIndex, currentBounds);
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Layout bounds of element {Index} are ({Bounds}).",
layoutId,
currentIndex,
currentBounds);
previousIndex = currentIndex;
currentIndex += step;
}
@ -394,20 +430,17 @@ namespace Avalonia.Layout
// If we did not reach the top or bottom of the extent, we realized one
// extra item before we knew we were outside the realization window. Do not
// account for that element in the indicies inside the realization window.
if (count > 0)
if (direction == GenerateDirection.Forward)
{
if (direction == GenerateDirection.Forward)
{
int dataCount = _context.ItemCount;
_lastRealizedDataIndexInsideRealizationWindow = previousIndex == dataCount - 1 ? dataCount - 1 : previousIndex - 1;
_lastRealizedDataIndexInsideRealizationWindow = Math.Max(0, _lastRealizedDataIndexInsideRealizationWindow);
}
else
{
int dataCount = _context.ItemCount;
_firstRealizedDataIndexInsideRealizationWindow = previousIndex == 0 ? 0 : previousIndex + 1;
_firstRealizedDataIndexInsideRealizationWindow = Math.Min(dataCount - 1, _firstRealizedDataIndexInsideRealizationWindow);
}
int dataCount = _context.ItemCount;
_lastRealizedDataIndexInsideRealizationWindow = previousIndex == dataCount - 1 ? dataCount - 1 : previousIndex - 1;
_lastRealizedDataIndexInsideRealizationWindow = Math.Max(0, _lastRealizedDataIndexInsideRealizationWindow);
}
else
{
int dataCount = _context.ItemCount;
_firstRealizedDataIndexInsideRealizationWindow = previousIndex == 0 ? 0 : previousIndex + 1;
_firstRealizedDataIndexInsideRealizationWindow = Math.Min(dataCount - 1, _firstRealizedDataIndexInsideRealizationWindow);
}
_elementManager.DiscardElementsOutsideWindow(direction == GenerateDirection.Forward, currentIndex);
@ -508,6 +541,7 @@ namespace Avalonia.Layout
lastDataIndex,
lastBounds);
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId} Extent: ({Bounds})", layoutId, extent);
return extent;
}
@ -676,6 +710,11 @@ namespace Avalonia.Layout
}
var element = _elementManager.GetAt(rangeIndex);
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Arranging element {Index} at ({Bounds})",
layoutId,
_elementManager.GetDataIndexFromRealizedRangeIndex(rangeIndex),
bounds);
element.Arrange(bounds);
}
}

2
src/Avalonia.Layout/LayoutHelper.cs

@ -118,7 +118,7 @@ namespace Avalonia.Layout
double newValue;
// If DPI == 1, don't use DPI-aware rounding.
if (!MathUtilities.AreClose(dpiScale, 1.0))
if (!MathUtilities.IsOne(dpiScale))
{
newValue = Math.Round(value * dpiScale) / dpiScale;

10
src/Avalonia.Layout/LayoutManager.cs

@ -23,10 +23,10 @@ namespace Avalonia.Layout
_executeLayoutPass = ExecuteLayoutPass;
}
public event EventHandler? LayoutUpdated;
public virtual event EventHandler? LayoutUpdated;
/// <inheritdoc/>
public void InvalidateMeasure(ILayoutable control)
public virtual void InvalidateMeasure(ILayoutable control)
{
control = control ?? throw new ArgumentNullException(nameof(control));
Dispatcher.UIThread.VerifyAccess();
@ -47,7 +47,7 @@ namespace Avalonia.Layout
}
/// <inheritdoc/>
public void InvalidateArrange(ILayoutable control)
public virtual void InvalidateArrange(ILayoutable control)
{
control = control ?? throw new ArgumentNullException(nameof(control));
Dispatcher.UIThread.VerifyAccess();
@ -67,7 +67,7 @@ namespace Avalonia.Layout
}
/// <inheritdoc/>
public void ExecuteLayoutPass()
public virtual void ExecuteLayoutPass()
{
const int MaxPasses = 3;
@ -131,7 +131,7 @@ namespace Avalonia.Layout
}
/// <inheritdoc/>
public void ExecuteInitialLayoutPass(ILayoutRoot root)
public virtual void ExecuteInitialLayoutPass(ILayoutRoot root)
{
try
{

2
src/Avalonia.Layout/LayoutQueue.cs

@ -77,7 +77,7 @@ namespace Avalonia.Layout
{
if (_shouldEnqueue(item.Key))
{
_loopQueueInfo[item.Key] = new Info() { Active = true, Count = item.Value.Count + 1 };
_loopQueueInfo[item.Key] = new Info() { Active = true, Count = 0 };
_inner.Enqueue(item.Key);
}
}

4
src/Avalonia.Layout/NonVirtualizingStackLayout.cs

@ -112,7 +112,9 @@ namespace Avalonia.Layout
u = (isVertical ? bounds.Bottom : bounds.Right) + spacing;
}
return new Size(bounds.Right, bounds.Bottom);
return new Size(
Math.Max(finalSize.Width, bounds.Width),
Math.Max(finalSize.Height, bounds.Height));
}
private static Rect LayoutVertical(ILayoutable element, double y, Size constraint)

8
src/Avalonia.Layout/StackLayout.cs

@ -6,6 +6,7 @@
using System;
using System.Collections.Specialized;
using Avalonia.Data;
using Avalonia.Logging;
namespace Avalonia.Layout
{
@ -107,8 +108,15 @@ namespace Avalonia.Layout
_orientation.MajorStart(extent) +
(remainingItems * averageElementSize));
}
else
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Estimating extent with no realized elements",
LayoutId);
}
}
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Extent is ({Size}). Based on average {Average}",
LayoutId, extent.Size, averageElementSize);
return extent;
}

7
src/Avalonia.Layout/UniformGridLayout.cs

@ -6,6 +6,7 @@
using System;
using System.Collections.Specialized;
using Avalonia.Data;
using Avalonia.Logging;
namespace Avalonia.Layout
{
@ -379,8 +380,14 @@ namespace Avalonia.Layout
ref extent,
_orientation.MajorEnd(lastRealizedLayoutBounds) - _orientation.MajorStart(extent) + (remainingItems / itemsPerLine) * lineSize);
}
else
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Estimating extent with no realized elements", LayoutId);
}
}
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Extent is ({Size}). Based on lineSize {LineSize} and items per line {ItemsPerLine}",
LayoutId, extent.Size, lineSize, itemsPerLine);
return extent;
}

4
src/Avalonia.Themes.Default/ToggleSwitch.xaml

@ -3,8 +3,8 @@
xmlns:sys="clr-namespace:System;assembly=netstandard">
<Styles.Resources>
<Thickness x:Key="ToggleSwitchTopHeaderMargin">0,0,0,6</Thickness>
<x:Double x:Key="ToggleSwitchPreContentMargin">6</x:Double>
<x:Double x:Key="ToggleSwitchPostContentMargin">6</x:Double>
<GridLength x:Key="ToggleSwitchPreContentMargin">6</GridLength>
<GridLength x:Key="ToggleSwitchPostContentMargin">6</GridLength>
<x:Double x:Key="ToggleSwitchThemeMinWidth">154</x:Double>
<x:Double x:Key="KnobOnPosition">20</x:Double>
<x:Double x:Key="KnobOffPosition">0</x:Double>

7
src/Avalonia.Themes.Fluent/Accents/BaseDark.xaml

@ -140,7 +140,12 @@
<SolidColorBrush x:Key="SystemControlTransientBorderBrush" Color="#000000" Opacity="0.36" />
<SolidColorBrush x:Key="SystemControlHighlightListLowRevealBackgroundBrush" Color="{StaticResource SystemListMediumColor}" />
<SolidColorBrush x:Key="SystemControlHighlightListMediumRevealBackgroundBrush" Color="{StaticResource SystemListMediumColor}" />
<SolidColorBrush x:Key="SystemControlHighlightAccentRevealBackgroundBrush" Color="{StaticResource SystemAccentColor}" />
<SolidColorBrush x:Key="SystemControlHighlightAccentRevealBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="SystemControlHighlightAccent3RevealBackgroundBrush" Color="{DynamicResource SystemAccentColor}" Opacity="0.6" />
<SolidColorBrush x:Key="SystemControlHighlightAccent2RevealBackgroundBrush" Color="{DynamicResource SystemAccentColor}" Opacity="0.8" />
<SolidColorBrush x:Key="SystemControlBackgroundChromeWhiteRevealBorderBrush" Color="{StaticResource SystemChromeWhiteColor}" />
<SolidColorBrush x:Key="SystemControlHighlightAltTransparentRevealBorderBrush" Color="Transparent" />
<SolidColorBrush x:Key="SystemControlBackgroundTransparentRevealBorderBrush" Color="Transparent" />
<!-- TODO implement AcrylicBrush -->
<!--<AcrylicBrush x:Key="SystemControlTransientBackgroundBrush" BackgroundSource="HostBackdrop" TintColor="{StaticResource SystemChromeAltHighColor}" TintOpacity="0.8" FallbackColor="{StaticResource SystemChromeMediumLowColor}" />-->
<SolidColorBrush x:Key="SystemControlTransientBackgroundBrush" Color="{StaticResource SystemChromeMediumLowColor}" />

7
src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml

@ -140,7 +140,12 @@
<SolidColorBrush x:Key="SystemControlTransientBorderBrush" Color="#000000" Opacity="0.14" />
<SolidColorBrush x:Key="SystemControlHighlightListLowRevealBackgroundBrush" Color="{StaticResource SystemListMediumColor}" />
<SolidColorBrush x:Key="SystemControlHighlightListMediumRevealBackgroundBrush" Color="{StaticResource SystemListMediumColor}" />
<SolidColorBrush x:Key="SystemControlHighlightAccentRevealBackgroundBrush" Color="{StaticResource SystemAccentColor}" />
<SolidColorBrush x:Key="SystemControlHighlightAccentRevealBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="SystemControlHighlightAccent3RevealBackgroundBrush" Color="{DynamicResource SystemAccentColor}" Opacity="0.6" />
<SolidColorBrush x:Key="SystemControlHighlightAccent2RevealBackgroundBrush" Color="{DynamicResource SystemAccentColor}" Opacity="0.8" />
<SolidColorBrush x:Key="SystemControlBackgroundChromeWhiteRevealBorderBrush" Color="{StaticResource SystemChromeWhiteColor}" />
<SolidColorBrush x:Key="SystemControlHighlightAltTransparentRevealBorderBrush" Color="Transparent" />
<SolidColorBrush x:Key="SystemControlBackgroundTransparentRevealBorderBrush" Color="Transparent" />
<!-- TODO implement AcrylicBrush -->
<!--<AcrylicBrush x:Key="SystemControlTransientBackgroundBrush" BackgroundSource="HostBackdrop" TintColor="{StaticResource SystemChromeAltHighColor}" TintOpacity="0.8" FallbackColor="{StaticResource SystemChromeMediumLowColor}" />-->
<SolidColorBrush x:Key="SystemControlTransientBackgroundBrush" Color="{StaticResource SystemChromeMediumLowColor}" />

52
src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml

@ -19,7 +19,6 @@
<Color x:Key="SystemChromeBlackMediumColor">#FF000000</Color>
<Color x:Key="SystemChromeBlackMediumLowColor">#FF000000</Color>
<Color x:Key="SystemChromeDisabledHighColor">#FF333333</Color>
<Color x:Key="SystemChromeDisabledLowColor">#FF9A9A9A</Color>
<Color x:Key="SystemChromeGrayColor">#FF808080</Color>
<Color x:Key="SystemChromeHighColor">#FF808080</Color>
<Color x:Key="SystemChromeLowColor">#FF151515</Color>
@ -471,5 +470,56 @@
<SolidColorBrush x:Key="PivotNavButtonPressedBackgroundThemeBrush" Color="#BD292929" />
<SolidColorBrush x:Key="PivotNavButtonPressedBorderThemeBrush" Color="#BD292929" />
<SolidColorBrush x:Key="PivotNavButtonPressedForegroundThemeBrush" Color="#FFFFFFFF" />
<!-- BaseResources for ScrollBar.xaml -->
<StaticResource x:Key="ScrollBarBackground" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBackgroundPointerOver" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBackgroundDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarForeground" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBorderBrush" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBorderBrushPointerOver" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBorderBrushDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBackground" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBackgroundPointerOver" ResourceKey="SystemControlBackgroundListLowBrush" />
<StaticResource x:Key="ScrollBarButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumBrush" />
<StaticResource x:Key="ScrollBarButtonBackgroundDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBorderBrush" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBorderBrushPointerOver" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBorderBrushPressed" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBorderBrushDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonArrowForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ScrollBarButtonArrowForegroundPointerOver" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ScrollBarButtonArrowForegroundPressed" ResourceKey="SystemControlForegroundAltHighBrush" />
<StaticResource x:Key="ScrollBarButtonArrowForegroundDisabled" ResourceKey="SystemControlForegroundBaseLowBrush" />
<StaticResource x:Key="ScrollBarThumbFill" ResourceKey="SystemControlForegroundChromeDisabledLowBrush" />
<StaticResource x:Key="ScrollBarThumbFillPointerOver" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="ScrollBarThumbFillPressed" ResourceKey="SystemControlBackgroundBaseMediumBrush" />
<StaticResource x:Key="ScrollBarThumbFillDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<SolidColorBrush x:Key="ScrollBarTrackFill" Color="{StaticResource SystemChromeMediumColor}" Opacity="0.9" />
<SolidColorBrush x:Key="ScrollBarTrackFillPointerOver" Color="{StaticResource SystemChromeMediumColor}" Opacity="0.9" />
<StaticResource x:Key="ScrollBarTrackFillDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="ScrollBarTrackStroke" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ScrollBarTrackStrokePointerOver" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ScrollBarTrackStrokeDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="ScrollBarPanningThumbBackgroundDisabled" ResourceKey="SystemControlDisabledChromeHighBrush" />
<StaticResource x:Key="ScrollBarThumbBackgroundColor" ResourceKey="SystemBaseLowColor" />
<StaticResource x:Key="ScrollBarPanningThumbBackgroundColor" ResourceKey="SystemChromeDisabledLowColor" />
<SolidColorBrush x:Key="ScrollBarThumbBackground" Color="{StaticResource ScrollBarThumbBackgroundColor}" />
<SolidColorBrush x:Key="ScrollBarPanningThumbBackground" Color="{StaticResource ScrollBarPanningThumbBackgroundColor}" />
<x:TimeSpan x:Key="ScrollBarExpandDuration">00:00:00.1</x:TimeSpan>
<x:String x:Key="ScrollBarExpandBeginTime">00:00:00.40</x:String>
<x:String x:Key="ScrollBarContractBeginTime">00:00:02.00</x:String>
<x:String x:Key="ScrollBarContractDelay">00:00:02</x:String>
<x:String x:Key="ScrollBarContractDuration">00:00:00.1</x:String>
<x:String x:Key="ScrollBarContractFinalKeyframe">00:00:02.1</x:String>
<x:Double x:Key="ScrollBarSize">16</x:Double>
<x:Double x:Key="SmallScrollThumbScale">0.125</x:Double>
<x:Double x:Key="SmallScrollThumbOffset">-2</x:Double>
<TransformOperations x:Key="VerticalSmallScrollThumbScaleTransform">scaleX(0.125) translateX(-2px)</TransformOperations>
<TransformOperations x:Key="HorizontalSmallScrollThumbScaleTransform">scaleY(0.125) translateY(-2px)</TransformOperations>
<x:Double x:Key="ScrollBarButtonArrowIconFontSize">8</x:Double>
<!-- BaseResources for ScrollViewer.xaml -->
<SolidColorBrush x:Key="ScrollViewerScrollBarsSeparatorBackground" Color="{StaticResource SystemChromeMediumColor}" Opacity="0.9" />
</Style.Resources>
</Style>

51
src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml

@ -473,5 +473,56 @@
<SolidColorBrush x:Key="PivotNavButtonPressedBackgroundThemeBrush" Color="#BD292929" />
<SolidColorBrush x:Key="PivotNavButtonPressedBorderThemeBrush" Color="#BD292929" />
<SolidColorBrush x:Key="PivotNavButtonPressedForegroundThemeBrush" Color="#FFFFFFFF" />
<!-- BaseResources for ScrollBar.xaml -->
<StaticResource x:Key="ScrollBarBackground" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBackgroundPointerOver" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBackgroundDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarForeground" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBorderBrush" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBorderBrushPointerOver" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBorderBrushDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBackground" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBackgroundPointerOver" ResourceKey="SystemControlBackgroundListLowBrush" />
<StaticResource x:Key="ScrollBarButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumBrush" />
<StaticResource x:Key="ScrollBarButtonBackgroundDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBorderBrush" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBorderBrushPointerOver" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBorderBrushPressed" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBorderBrushDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonArrowForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ScrollBarButtonArrowForegroundPointerOver" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ScrollBarButtonArrowForegroundPressed" ResourceKey="SystemControlForegroundAltHighBrush" />
<StaticResource x:Key="ScrollBarButtonArrowForegroundDisabled" ResourceKey="SystemControlForegroundBaseLowBrush" />
<StaticResource x:Key="ScrollBarThumbFill" ResourceKey="SystemControlForegroundChromeDisabledLowBrush" />
<StaticResource x:Key="ScrollBarThumbFillPointerOver" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="ScrollBarThumbFillPressed" ResourceKey="SystemControlBackgroundBaseMediumBrush" />
<StaticResource x:Key="ScrollBarThumbFillDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<SolidColorBrush x:Key="ScrollBarTrackFill" Color="{StaticResource SystemChromeMediumColor}" Opacity="0.9" />
<SolidColorBrush x:Key="ScrollBarTrackFillPointerOver" Color="{StaticResource SystemChromeMediumColor}" Opacity="0.9" />
<StaticResource x:Key="ScrollBarTrackFillDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="ScrollBarTrackStroke" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ScrollBarTrackStrokePointerOver" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ScrollBarTrackStrokeDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="ScrollBarPanningThumbBackgroundDisabled" ResourceKey="SystemControlDisabledChromeHighBrush" />
<StaticResource x:Key="ScrollBarThumbBackgroundColor" ResourceKey="SystemBaseLowColor" />
<StaticResource x:Key="ScrollBarPanningThumbBackgroundColor" ResourceKey="SystemChromeDisabledLowColor" />
<SolidColorBrush x:Key="ScrollBarThumbBackground" Color="{StaticResource ScrollBarThumbBackgroundColor}" />
<SolidColorBrush x:Key="ScrollBarPanningThumbBackground" Color="{StaticResource ScrollBarPanningThumbBackgroundColor}" />
<x:String x:Key="ScrollBarExpandDuration">00:00:00.1</x:String>
<x:String x:Key="ScrollBarExpandBeginTime">00:00:00.40</x:String>
<x:String x:Key="ScrollBarContractBeginTime">00:00:02.00</x:String>
<x:String x:Key="ScrollBarContractDelay">00:00:02</x:String>
<x:String x:Key="ScrollBarContractDuration">00:00:00.1</x:String>
<x:String x:Key="ScrollBarContractFinalKeyframe">00:00:02.1</x:String>
<x:Double x:Key="ScrollBarSize">16</x:Double>
<x:Double x:Key="SmallScrollThumbScale">0.125</x:Double>
<x:Double x:Key="SmallScrollThumbOffset">-2</x:Double>
<TransformOperations x:Key="VerticalSmallScrollThumbScaleTransform">scaleX(0.125) translateX(-2px)</TransformOperations>
<TransformOperations x:Key="HorizontalSmallScrollThumbScaleTransform">scaleY(0.125) translateY(-2px)</TransformOperations>
<x:Double x:Key="ScrollBarButtonArrowIconFontSize">8</x:Double>
<!-- BaseResources for ScrollViewer.xaml -->
<SolidColorBrush x:Key="ScrollViewerScrollBarsSeparatorBackground" Color="{StaticResource SystemChromeMediumColor}" Opacity="0.9" />
</Style.Resources>
</Style>

93
src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml

@ -789,5 +789,98 @@
<SolidColorBrush x:Key="PivotNavButtonPressedBackgroundThemeBrush" Color="#BD292929" />
<SolidColorBrush x:Key="PivotNavButtonPressedBorderThemeBrush" Color="#BD292929" />
<SolidColorBrush x:Key="PivotNavButtonPressedForegroundThemeBrush" Color="#FFFFFFFF" />
<!-- Resources for ScrollBar.xaml -->
<x:Double x:Key="ScrollBarTrackBorderThemeThickness">0</x:Double>
<Thickness x:Key="ScrollBarPanningBorderThemeThickness">1</Thickness>
<StaticResource x:Key="ScrollBarBackground" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBackgroundPointerOver" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBackgroundDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarForeground" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBorderBrush" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBorderBrushPointerOver" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBorderBrushDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBackground" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBackgroundPointerOver" ResourceKey="SystemControlBackgroundListLowBrush" />
<StaticResource x:Key="ScrollBarButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumBrush" />
<StaticResource x:Key="ScrollBarButtonBackgroundDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBorderBrush" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBorderBrushPointerOver" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBorderBrushPressed" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBorderBrushDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonArrowForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ScrollBarButtonArrowForegroundPointerOver" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ScrollBarButtonArrowForegroundPressed" ResourceKey="SystemControlForegroundAltHighBrush" />
<StaticResource x:Key="ScrollBarButtonArrowForegroundDisabled" ResourceKey="SystemControlForegroundBaseLowBrush" />
<StaticResource x:Key="ScrollBarThumbFill" ResourceKey="SystemControlForegroundChromeDisabledLowBrush" />
<StaticResource x:Key="ScrollBarThumbFillPointerOver" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="ScrollBarThumbFillPressed" ResourceKey="SystemControlBackgroundBaseMediumBrush" />
<StaticResource x:Key="ScrollBarThumbFillDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<SolidColorBrush x:Key="ScrollBarTrackFill" Color="{StaticResource SystemChromeMediumColor}" Opacity="0.9" />
<SolidColorBrush x:Key="ScrollBarTrackFillPointerOver" Color="{StaticResource SystemChromeMediumColor}" Opacity="0.9" />
<StaticResource x:Key="ScrollBarTrackFillDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="ScrollBarTrackStroke" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ScrollBarTrackStrokePointerOver" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ScrollBarTrackStrokeDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="ScrollBarThumbBackgroundColor" ResourceKey="SystemBaseLowColor" />
<StaticResource x:Key="ScrollBarPanningThumbBackgroundColor" ResourceKey="SystemChromeDisabledLowColor" />
<SolidColorBrush x:Key="ScrollBarThumbBackground" Color="{StaticResource ScrollBarThumbBackgroundColor}" />
<SolidColorBrush x:Key="ScrollBarPanningThumbBackground" Color="{StaticResource ScrollBarPanningThumbBackgroundColor}" />
<StaticResource x:Key="ScrollBarPanningThumbBackgroundDisabled" ResourceKey="SystemControlDisabledChromeHighBrush" />
<SolidColorBrush x:Key="ScrollBarButtonForegroundThemeBrush" Color="#99000000" />
<SolidColorBrush x:Key="ScrollBarButtonPointerOverBackgroundThemeBrush" Color="#FFDADADA" />
<SolidColorBrush x:Key="ScrollBarButtonPointerOverBorderThemeBrush" Color="#FFDADADA" />
<SolidColorBrush x:Key="ScrollBarButtonPointerOverForegroundThemeBrush" Color="#FF000000" />
<SolidColorBrush x:Key="ScrollBarButtonPressedBackgroundThemeBrush" Color="#99000000" />
<SolidColorBrush x:Key="ScrollBarButtonPressedBorderThemeBrush" Color="#99000000" />
<SolidColorBrush x:Key="ScrollBarButtonPressedForegroundThemeBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="ScrollBarPanningBackgroundThemeBrush" Color="#FFCDCDCD" />
<SolidColorBrush x:Key="ScrollBarPanningBorderThemeBrush" Color="#7D9A9A9A" />
<SolidColorBrush x:Key="ScrollBarThumbBackgroundThemeBrush" Color="#FFCDCDCD" />
<SolidColorBrush x:Key="ScrollBarThumbBorderThemeBrush" Color="#3B555555" />
<SolidColorBrush x:Key="ScrollBarThumbPointerOverBackgroundThemeBrush" Color="#FFDADADA" />
<SolidColorBrush x:Key="ScrollBarThumbPointerOverBorderThemeBrush" Color="#6BB7B7B7" />
<SolidColorBrush x:Key="ScrollBarThumbPressedBackgroundThemeBrush" Color="#99000000" />
<SolidColorBrush x:Key="ScrollBarThumbPressedBorderThemeBrush" Color="#ED555555" />
<SolidColorBrush x:Key="ScrollBarTrackBackgroundThemeBrush" Color="#59D5D5D5" />
<SolidColorBrush x:Key="ScrollBarTrackBorderThemeBrush" Color="#59D5D5D5" />
<x:String x:Key="ScrollBarExpandDuration">00:00:00.1</x:String>
<x:String x:Key="ScrollBarContractDelay">00:00:02</x:String>
<x:String x:Key="ScrollBarContractDuration">00:00:00.1</x:String>
<x:String x:Key="ScrollBarContractFinalKeyframe">00:00:02.1</x:String>
<x:Double x:Key="ScrollBarSize">16</x:Double>
<x:Double x:Key="ScrollBarButtonArrowIconFontSize">8</x:Double>
<x:String x:Key="ScrollBarExpandBeginTime">00:00:00.40</x:String>
<x:String x:Key="ScrollBarContractBeginTime">00:00:02.00</x:String>
<!-- Resources for TreeViewItem.xaml -->
<StaticResource x:Key="TreeViewItemBackground" ResourceKey="SystemControlTransparentRevealBackgroundBrush" />
<StaticResource x:Key="TreeViewItemBackgroundPointerOver" ResourceKey="SystemControlHighlightListLowRevealBackgroundBrush" />
<StaticResource x:Key="TreeViewItemBackgroundPressed" ResourceKey="SystemControlHighlightListMediumRevealBackgroundBrush" />
<StaticResource x:Key="TreeViewItemBackgroundDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="TreeViewItemBackgroundSelected" ResourceKey="SystemControlHighlightAccent3RevealBackgroundBrush" />
<StaticResource x:Key="TreeViewItemBackgroundSelectedPointerOver" ResourceKey="SystemControlHighlightAccent2RevealBackgroundBrush" />
<StaticResource x:Key="TreeViewItemBackgroundSelectedPressed" ResourceKey="SystemControlHighlightListMediumRevealBackgroundBrush" />
<StaticResource x:Key="TreeViewItemBackgroundSelectedDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="TreeViewItemForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="TreeViewItemForegroundPointerOver" ResourceKey="SystemControlHighlightAltBaseHighBrush" />
<StaticResource x:Key="TreeViewItemForegroundPressed" ResourceKey="SystemControlHighlightAltBaseHighBrush" />
<StaticResource x:Key="TreeViewItemForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="TreeViewItemForegroundSelected" ResourceKey="SystemControlHighlightAltBaseHighBrush" />
<StaticResource x:Key="TreeViewItemForegroundSelectedPointerOver" ResourceKey="SystemControlHighlightAltBaseHighBrush" />
<StaticResource x:Key="TreeViewItemForegroundSelectedPressed" ResourceKey="SystemControlHighlightAltBaseHighBrush" />
<StaticResource x:Key="TreeViewItemForegroundSelectedDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="TreeViewItemBorderBrush" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="TreeViewItemBorderBrushPointerOver" ResourceKey="SystemControlHighlightAltTransparentRevealBorderBrush" />
<StaticResource x:Key="TreeViewItemBorderBrushPressed" ResourceKey="SystemControlHighlightAltTransparentRevealBorderBrush" />
<StaticResource x:Key="TreeViewItemBorderBrushDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="TreeViewItemBorderBrushSelected" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="TreeViewItemBorderBrushSelectedPointerOver" ResourceKey="SystemControlBackgroundTransparentRevealBorderBrush" />
<StaticResource x:Key="TreeViewItemBorderBrushSelectedPressed" ResourceKey="SystemControlBackgroundTransparentRevealBorderBrush" />
<StaticResource x:Key="TreeViewItemBorderBrushSelectedDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="TreeViewItemCheckBoxBackgroundSelected" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="TreeViewItemCheckBoxBorderSelected" ResourceKey="SystemControlForegroundBaseMediumHighBrush" />
<StaticResource x:Key="TreeViewItemCheckGlyphSelected" ResourceKey="SystemControlForegroundBaseMediumHighBrush" />
<Thickness x:Key="TreeViewItemBorderThemeThickness">1</Thickness>
<x:Double x:Key="TreeViewItemMinHeight">32</x:Double>
</Style.Resources>
</Style>

97
src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml

@ -366,8 +366,8 @@
<x:Double x:Key="AutoCompleteBoxIconFontSize">12</x:Double>
<!-- Resources for CheckBox.xaml -->
<x:Double x:Key="CheckBoxBorderThemeThickness">1</x:Double>
<x:Double x:Key="CheckBoxCheckedStrokeThickness">0</x:Double>
<Thickness x:Key="CheckBoxBorderThemeThickness">1</Thickness>
<Thickness x:Key="CheckBoxCheckedStrokeThickness">0</Thickness>
<StaticResource x:Key="CheckBoxForegroundUnchecked" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="CheckBoxForegroundUncheckedPointerOver" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="CheckBoxForegroundUncheckedPressed" ResourceKey="SystemControlForegroundBaseHighBrush" />
@ -787,5 +787,98 @@
<SolidColorBrush x:Key="PivotNavButtonPressedBackgroundThemeBrush" Color="#BD292929" />
<SolidColorBrush x:Key="PivotNavButtonPressedBorderThemeBrush" Color="#BD292929" />
<SolidColorBrush x:Key="PivotNavButtonPressedForegroundThemeBrush" Color="#FFFFFFFF" />
<!-- Resources for ScrollBar.xaml -->
<x:Double x:Key="ScrollBarTrackBorderThemeThickness">0</x:Double>
<Thickness x:Key="ScrollBarPanningBorderThemeThickness">1</Thickness>
<StaticResource x:Key="ScrollBarBackground" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBackgroundPointerOver" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBackgroundDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarForeground" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBorderBrush" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBorderBrushPointerOver" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarBorderBrushDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBackground" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBackgroundPointerOver" ResourceKey="SystemControlBackgroundListLowBrush" />
<StaticResource x:Key="ScrollBarButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumBrush" />
<StaticResource x:Key="ScrollBarButtonBackgroundDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBorderBrush" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBorderBrushPointerOver" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBorderBrushPressed" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonBorderBrushDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ScrollBarButtonArrowForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ScrollBarButtonArrowForegroundPointerOver" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ScrollBarButtonArrowForegroundPressed" ResourceKey="SystemControlForegroundAltHighBrush" />
<StaticResource x:Key="ScrollBarButtonArrowForegroundDisabled" ResourceKey="SystemControlForegroundBaseLowBrush" />
<StaticResource x:Key="ScrollBarThumbFill" ResourceKey="SystemControlForegroundChromeDisabledLowBrush" />
<StaticResource x:Key="ScrollBarThumbFillPointerOver" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="ScrollBarThumbFillPressed" ResourceKey="SystemControlBackgroundBaseMediumBrush" />
<StaticResource x:Key="ScrollBarThumbFillDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<SolidColorBrush x:Key="ScrollBarTrackFill" Color="{StaticResource SystemChromeMediumColor}" Opacity="0.9" />
<SolidColorBrush x:Key="ScrollBarTrackFillPointerOver" Color="{StaticResource SystemChromeMediumColor}" Opacity="0.9" />
<StaticResource x:Key="ScrollBarTrackFillDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="ScrollBarTrackStroke" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ScrollBarTrackStrokePointerOver" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ScrollBarTrackStrokeDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<SolidColorBrush x:Key="ScrollBarThumbBackground" Color="{StaticResource ScrollBarThumbBackgroundColor}" />
<SolidColorBrush x:Key="ScrollBarPanningThumbBackground" Color="{StaticResource ScrollBarPanningThumbBackgroundColor}" />
<StaticResource x:Key="ScrollBarPanningThumbBackgroundDisabled" ResourceKey="SystemControlDisabledChromeHighBrush" />
<SolidColorBrush x:Key="ScrollBarButtonForegroundThemeBrush" Color="#99000000" />
<SolidColorBrush x:Key="ScrollBarButtonPointerOverBackgroundThemeBrush" Color="#FFDADADA" />
<SolidColorBrush x:Key="ScrollBarButtonPointerOverBorderThemeBrush" Color="#FFDADADA" />
<SolidColorBrush x:Key="ScrollBarButtonPointerOverForegroundThemeBrush" Color="#FF000000" />
<SolidColorBrush x:Key="ScrollBarButtonPressedBackgroundThemeBrush" Color="#99000000" />
<SolidColorBrush x:Key="ScrollBarButtonPressedBorderThemeBrush" Color="#99000000" />
<SolidColorBrush x:Key="ScrollBarButtonPressedForegroundThemeBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="ScrollBarPanningBackgroundThemeBrush" Color="#FFCDCDCD" />
<SolidColorBrush x:Key="ScrollBarPanningBorderThemeBrush" Color="#7D9A9A9A" />
<SolidColorBrush x:Key="ScrollBarThumbBackgroundThemeBrush" Color="#FFCDCDCD" />
<SolidColorBrush x:Key="ScrollBarThumbBorderThemeBrush" Color="#3B555555" />
<SolidColorBrush x:Key="ScrollBarThumbPointerOverBackgroundThemeBrush" Color="#FFDADADA" />
<SolidColorBrush x:Key="ScrollBarThumbPointerOverBorderThemeBrush" Color="#6BB7B7B7" />
<SolidColorBrush x:Key="ScrollBarThumbPressedBackgroundThemeBrush" Color="#99000000" />
<SolidColorBrush x:Key="ScrollBarThumbPressedBorderThemeBrush" Color="#ED555555" />
<SolidColorBrush x:Key="ScrollBarTrackBackgroundThemeBrush" Color="#59D5D5D5" />
<SolidColorBrush x:Key="ScrollBarTrackBorderThemeBrush" Color="#59D5D5D5" />
<StaticResource x:Key="ScrollBarThumbBackgroundColor" ResourceKey="SystemBaseLowColor" />
<StaticResource x:Key="ScrollBarPanningThumbBackgroundColor" ResourceKey="SystemChromeDisabledLowColor" />
<x:String x:Key="ScrollBarExpandDuration">00:00:00.1</x:String>
<x:String x:Key="ScrollBarContractDelay">00:00:02</x:String>
<x:String x:Key="ScrollBarContractDuration">00:00:00.1</x:String>
<x:String x:Key="ScrollBarContractFinalKeyframe">00:00:02.1</x:String>
<x:Double x:Key="ScrollBarSize">16</x:Double>
<x:Double x:Key="ScrollBarButtonArrowIconFontSize">8</x:Double>
<x:String x:Key="ScrollBarExpandBeginTime">00:00:00.40</x:String>
<x:String x:Key="ScrollBarContractBeginTime">00:00:02.00</x:String>
<!-- Resources for TreeViewItem.xaml -->
<StaticResource x:Key="TreeViewItemBackground" ResourceKey="SystemControlTransparentRevealBackgroundBrush" />
<StaticResource x:Key="TreeViewItemBackgroundPointerOver" ResourceKey="SystemControlHighlightListLowRevealBackgroundBrush" />
<StaticResource x:Key="TreeViewItemBackgroundPressed" ResourceKey="SystemControlHighlightListMediumRevealBackgroundBrush" />
<StaticResource x:Key="TreeViewItemBackgroundDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="TreeViewItemBackgroundSelected" ResourceKey="SystemControlHighlightAccent3RevealBackgroundBrush" />
<StaticResource x:Key="TreeViewItemBackgroundSelectedPointerOver" ResourceKey="SystemControlHighlightAccent2RevealBackgroundBrush" />
<StaticResource x:Key="TreeViewItemBackgroundSelectedPressed" ResourceKey="SystemControlHighlightListMediumRevealBackgroundBrush" />
<StaticResource x:Key="TreeViewItemBackgroundSelectedDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="TreeViewItemForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="TreeViewItemForegroundPointerOver" ResourceKey="SystemControlHighlightAltBaseHighBrush" />
<StaticResource x:Key="TreeViewItemForegroundPressed" ResourceKey="SystemControlHighlightAltBaseHighBrush" />
<StaticResource x:Key="TreeViewItemForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="TreeViewItemForegroundSelected" ResourceKey="SystemControlHighlightAltBaseHighBrush" />
<StaticResource x:Key="TreeViewItemForegroundSelectedPointerOver" ResourceKey="SystemControlHighlightAltBaseHighBrush" />
<StaticResource x:Key="TreeViewItemForegroundSelectedPressed" ResourceKey="SystemControlHighlightAltBaseHighBrush" />
<StaticResource x:Key="TreeViewItemForegroundSelectedDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="TreeViewItemBorderBrush" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="TreeViewItemBorderBrushPointerOver" ResourceKey="SystemControlHighlightAltTransparentRevealBorderBrush" />
<StaticResource x:Key="TreeViewItemBorderBrushPressed" ResourceKey="SystemControlHighlightAltTransparentRevealBorderBrush" />
<StaticResource x:Key="TreeViewItemBorderBrushDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="TreeViewItemBorderBrushSelected" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="TreeViewItemBorderBrushSelectedPointerOver" ResourceKey="SystemControlBackgroundTransparentRevealBorderBrush" />
<StaticResource x:Key="TreeViewItemBorderBrushSelectedPressed" ResourceKey="SystemControlBackgroundTransparentRevealBorderBrush" />
<StaticResource x:Key="TreeViewItemBorderBrushSelectedDisabled" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="TreeViewItemCheckBoxBackgroundSelected" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="TreeViewItemCheckBoxBorderSelected" ResourceKey="SystemControlForegroundBaseMediumHighBrush" />
<StaticResource x:Key="TreeViewItemCheckGlyphSelected" ResourceKey="SystemControlForegroundBaseMediumHighBrush" />
<Thickness x:Key="TreeViewItemBorderThemeThickness">1</Thickness>
<x:Double x:Key="TreeViewItemMinHeight">32</x:Double>
</Style.Resources>
</Style>

374
src/Avalonia.Themes.Fluent/ScrollBar.xaml

@ -1,142 +1,302 @@
<Styles xmlns="https://github.com/avaloniaui">
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="ScrollBar">
<Setter Property="Cursor" Value="Arrow" />
<Setter Property="MinWidth" Value="{DynamicResource ScrollBarSize}" />
<Setter Property="MinHeight" Value="{DynamicResource ScrollBarSize}" />
<Setter Property="Background" Value="{DynamicResource ScrollBarBackground}" />
<Setter Property="Foreground" Value="{DynamicResource ScrollBarForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource ScrollBarBorderBrush}" />
</Style>
<Style Selector="ScrollBar:vertical">
<Setter Property="Template">
<ControlTemplate>
<Border Background="{DynamicResource ThemeControlMidBrush}"
UseLayoutRounding="False">
<Grid RowDefinitions="Auto,*,Auto">
<RepeatButton Name="PART_LineUpButton" HorizontalAlignment="Center"
Classes="repeat"
Grid.Row="0"
Focusable="False"
MinHeight="{DynamicResource ScrollBarThickness}">
<Path Data="M 0 4 L 8 4 L 4 0 Z" />
</RepeatButton>
<Track Grid.Row="1"
Grid.Column="1"
Minimum="{TemplateBinding Minimum}"
Maximum="{TemplateBinding Maximum}"
Value="{TemplateBinding Value, Mode=TwoWay}"
ViewportSize="{TemplateBinding ViewportSize}"
Orientation="{TemplateBinding Orientation}"
IsDirectionReversed="True">
<Track.DecreaseButton>
<RepeatButton Name="PART_PageUpButton"
Classes="repeattrack"
Focusable="False"/>
</Track.DecreaseButton>
<Track.IncreaseButton>
<RepeatButton Name="PART_PageDownButton"
Classes="repeattrack"
Focusable="False"/>
</Track.IncreaseButton>
<Thumb Name="thumb"/>
</Track>
<RepeatButton Name="PART_LineDownButton" HorizontalAlignment="Center"
Classes="repeat"
Grid.Row="2"
Grid.Column="2"
Focusable="False"
MinHeight="{DynamicResource ScrollBarThickness}">
<Path Data="M 0 0 L 4 4 L 8 0 Z" />
</RepeatButton>
</Grid>
</Border>
<Grid x:Name="Root">
<Border x:Name="VerticalRoot"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}">
<Grid RowDefinitions="Auto,*,Auto">
<Rectangle x:Name="TrackRect" Grid.RowSpan="3" Margin="0" />
<RepeatButton Name="PART_LineUpButton"
HorizontalAlignment="Center"
Classes="line up"
Grid.Row="0"
Focusable="False"
MinWidth="{DynamicResource ScrollBarSize}"
Height="{DynamicResource ScrollBarSize}" />
<Track Grid.Row="1"
Minimum="{TemplateBinding Minimum}"
Maximum="{TemplateBinding Maximum}"
Value="{TemplateBinding Value, Mode=TwoWay}"
ViewportSize="{TemplateBinding ViewportSize}"
Orientation="{TemplateBinding Orientation}"
IsDirectionReversed="True">
<Track.DecreaseButton>
<RepeatButton Name="PART_PageUpButton"
Classes="largeIncrease"
Focusable="False" />
</Track.DecreaseButton>
<Track.IncreaseButton>
<RepeatButton Name="PART_PageDownButton"
Classes="largeIncrease"
Focusable="False" />
</Track.IncreaseButton>
<Thumb Classes="thumb"
Opacity="1"
Width="{DynamicResource ScrollBarSize}"
MinHeight="{DynamicResource ScrollBarSize}"
RenderTransformOrigin="100%,50%" />
</Track>
<RepeatButton Name="PART_LineDownButton"
HorizontalAlignment="Center"
Classes="line down"
Grid.Row="2"
Focusable="False"
MinWidth="{DynamicResource ScrollBarSize}"
Height="{DynamicResource ScrollBarSize}" />
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="ScrollBar:horizontal">
<Setter Property="Height" Value="{DynamicResource ScrollBarThickness}" />
<Setter Property="Template">
<ControlTemplate>
<Border Background="{DynamicResource ThemeControlMidBrush}"
UseLayoutRounding="False">
<Grid ColumnDefinitions="Auto,*,Auto">
<RepeatButton Name="PART_LineUpButton" VerticalAlignment="Center"
Classes="repeat"
Grid.Row="0"
Grid.Column="0"
Focusable="False"
MinWidth="{DynamicResource ScrollBarThickness}">
<Path Data="M 4 0 L 4 8 L 0 4 Z" />
</RepeatButton>
<Track Grid.Row="1"
Grid.Column="1"
Minimum="{TemplateBinding Minimum}"
Maximum="{TemplateBinding Maximum}"
Value="{TemplateBinding Value, Mode=TwoWay}"
ViewportSize="{TemplateBinding ViewportSize}"
Orientation="{TemplateBinding Orientation}">
<Track.DecreaseButton>
<RepeatButton Name="PART_PageUpButton"
Classes="repeattrack"
Focusable="False"/>
</Track.DecreaseButton>
<Track.IncreaseButton>
<RepeatButton Name="PART_PageDownButton"
Classes="repeattrack"
Focusable="False"/>
</Track.IncreaseButton>
<Thumb Name="thumb"/>
</Track>
<RepeatButton Name="PART_LineDownButton" VerticalAlignment="Center"
Classes="repeat"
Grid.Row="2"
Grid.Column="2"
Focusable="False"
MinWidth="{DynamicResource ScrollBarThickness}">
<Path Data="M 0 0 L 4 4 L 0 8 Z" />
</RepeatButton>
</Grid>
</Border>
<Grid x:Name="Root">
<Border x:Name="HorizontalRoot"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}">
<Grid ColumnDefinitions="Auto,*,Auto">
<Rectangle x:Name="TrackRect" Grid.ColumnSpan="3" Margin="0" />
<RepeatButton Name="PART_LineUpButton"
VerticalAlignment="Center"
Classes="line up"
Grid.Column="0"
Focusable="False"
MinHeight="{DynamicResource ScrollBarSize}"
Width="{DynamicResource ScrollBarSize}" />
<Track Grid.Column="1"
Minimum="{TemplateBinding Minimum}"
Maximum="{TemplateBinding Maximum}"
Value="{TemplateBinding Value, Mode=TwoWay}"
ViewportSize="{TemplateBinding ViewportSize}"
Orientation="{TemplateBinding Orientation}">
<Track.DecreaseButton>
<RepeatButton Name="PART_PageUpButton"
Classes="largeIncrease"
Focusable="False" />
</Track.DecreaseButton>
<Track.IncreaseButton>
<RepeatButton Name="PART_PageDownButton"
Classes="largeIncrease"
Focusable="False" />
</Track.IncreaseButton>
<Thumb Classes="thumb"
Opacity="1"
Height="{DynamicResource ScrollBarSize}"
MinWidth="{DynamicResource ScrollBarSize}"
RenderTransformOrigin="50%,100%" />
</Track>
<RepeatButton Name="PART_LineDownButton"
VerticalAlignment="Center"
Classes="line down"
Grid.Column="2"
Focusable="False"
MinHeight="{DynamicResource ScrollBarSize}"
Width="{DynamicResource ScrollBarSize}" />
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="ScrollBar /template/ Thumb#thumb">
<Setter Property="Background" Value="{DynamicResource ThemeControlMidHighBrush}"/>
<Style Selector="ScrollBar[IsExpanded=true] /template/ Grid#Root">
<Setter Property="Background" Value="{DynamicResource ScrollBarBackgroundPointerOver}" />
</Style>
<Style Selector="ScrollBar /template/ Thumb.thumb">
<Setter Property="Background" Value="{DynamicResource ScrollBarPanningThumbBackground}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="{TemplateBinding Background}"/>
<Border x:Name="ThumbVisual" Background="{TemplateBinding Background}" />
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Transitions">
<Transitions>
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.1" />
</Transitions>
</Setter>
</Style>
<Style Selector="ScrollBar /template/ Thumb#thumb:pointerover">
<Setter Property="Background" Value="{DynamicResource ThemeControlHighBrush}"/>
<Style Selector="ScrollBar:vertical /template/ Thumb.thumb">
<Setter Property="RenderTransform" Value="{DynamicResource VerticalSmallScrollThumbScaleTransform}" />
</Style>
<Style Selector="ScrollBar /template/ Thumb#thumb:pressed">
<Setter Property="Background" Value="{DynamicResource ThemeControlVeryHighBrush}"/>
<Style Selector="ScrollBar:horizontal /template/ Thumb.thumb">
<Setter Property="RenderTransform" Value="{DynamicResource HorizontalSmallScrollThumbScaleTransform}" />
</Style>
<Style Selector="ScrollBar:horizontal /template/ Thumb#thumb">
<Setter Property="MinWidth" Value="{DynamicResource ScrollBarThickness}" />
<Setter Property="Height" Value="{DynamicResource ScrollBarThumbThickness}" />
<Style Selector="ScrollBar[IsExpanded=true] /template/ Thumb.thumb">
<Setter Property="RenderTransform" Value="none" />
<Setter Property="Background" Value="{DynamicResource ScrollBarThumbBackgroundColor}" />
</Style>
<Style Selector="ScrollBar:vertical">
<Setter Property="Width" Value="{DynamicResource ScrollBarThickness}" />
<Style Selector="ScrollBar /template/ Thumb.thumb /template/ Border#ThumbVisual">
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
<Setter Property="Transitions">
<Transitions>
<CornerRadiusTransition Property="CornerRadius" Duration="0:0:0.1" />
</Transitions>
</Setter>
</Style>
<Style Selector="ScrollBar:vertical /template/ Thumb#thumb">
<Setter Property="MinHeight" Value="{DynamicResource ScrollBarThickness}" />
<Setter Property="Width" Value="{DynamicResource ScrollBarThumbThickness}" />
<Style Selector="ScrollBar[IsExpanded=true] /template/ Thumb.thumb /template/ Border#ThumbVisual">
<Setter Property="CornerRadius" Value="0" />
</Style>
<Style Selector="ScrollBar /template/ RepeatButton.repeat">
<Setter Property="Padding" Value="2" />
<Setter Property="BorderThickness" Value="0" />
<Style Selector="ScrollBar /template/ Thumb.thumb:pointerover">
<Setter Property="Background" Value="{DynamicResource ScrollBarThumbFillPointerOver}" />
</Style>
<Style Selector="ScrollBar /template/ Thumb.thumb:pressed">
<Setter Property="Background" Value="{DynamicResource ScrollBarThumbFillPressed}" />
</Style>
<Style Selector="ScrollBar /template/ RepeatButton.repeattrack">
<Style Selector="ScrollBar /template/ Thumb.thumb:disabled">
<Setter Property="Background" Value="{DynamicResource ScrollBarThumbFillDisabled}" />
</Style>
<Style Selector="ScrollBar /template/ RepeatButton.line">
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}" />
<Border x:Name="Root">
<Viewbox Width="{DynamicResource ScrollBarButtonArrowIconFontSize}"
Height="{DynamicResource ScrollBarButtonArrowIconFontSize}">
<Path x:Name="Arrow"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Width="20" Height="20" />
</Viewbox>
</Border>
</ControlTemplate>
</Setter>
<Setter Property="Opacity" Value="0" />
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.1" />
</Transitions>
</Setter>
</Style>
<Style Selector="ScrollBar /template/ RepeatButton.line /template/ Border#Root" >
<Setter Property="Background" Value="{DynamicResource ScrollBarButtonBackground}" />
<Setter Property="BorderBrush" Value="{DynamicResource ScrollBarButtonBorderBrush}" />
</Style>
<Style Selector="ScrollBar /template/ RepeatButton.line:pointerover /template/ Border#Root" >
<Setter Property="Background" Value="{DynamicResource ScrollBarButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ScrollBarButtonBorderBrushPointerOver}" />
</Style>
<Style Selector="ScrollBar /template/ RepeatButton.line:pressed /template/ Border#Root" >
<Setter Property="Background" Value="{DynamicResource ScrollBarButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ScrollBarButtonBorderBrushPressed}" />
</Style>
<Style Selector="ScrollBar /template/ RepeatButton.line:disabled /template/ Border#Root" >
<Setter Property="Background" Value="{DynamicResource ScrollBarButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ScrollBarButtonBorderBrushDisabled}" />
</Style>
<Style Selector="ScrollBar /template/ RepeatButton.line /template/ Path#Arrow" >
<Setter Property="Fill" Value="{DynamicResource ScrollBarButtonArrowForeground}" />
</Style>
<Style Selector="ScrollBar /template/ RepeatButton.line:pointerover /template/ Path#Arrow" >
<Setter Property="Fill" Value="{DynamicResource ScrollBarButtonArrowForegroundPointerOver}" />
</Style>
<Style Selector="ScrollBar /template/ RepeatButton.line:pressed /template/ Path#Arrow" >
<Setter Property="Fill" Value="{DynamicResource ScrollBarButtonArrowForegroundPressed}" />
</Style>
<Style Selector="ScrollBar /template/ RepeatButton.line:disabled /template/ Path#Arrow" >
<Setter Property="Fill" Value="{DynamicResource ScrollBarButtonArrowForegroundDisabled}" />
</Style>
<Style Selector="ScrollBar[IsExpanded=true] /template/ RepeatButton.line">
<Setter Property="Opacity" Value="1" />
</Style>
<Style Selector="ScrollBar /template/ RepeatButton > Path">
<Setter Property="Fill" Value="{DynamicResource ThemeForegroundLowBrush}" />
<Style Selector="ScrollBar:vertical /template/ RepeatButton.line.up /template/ Path">
<Setter Property="Data"
Value="M 19.091797 14.970703 L 10 5.888672 L 0.908203 14.970703 L 0.029297 14.091797 L 10 4.111328 L 19.970703 14.091797 Z" />
</Style>
<Style Selector="ScrollBar /template/ RepeatButton:pointerover > Path">
<Setter Property="Fill" Value="{DynamicResource ThemeAccentBrush}" />
<Style Selector="ScrollBar:vertical /template/ RepeatButton.line.down /template/ Path">
<Setter Property="Data"
Value="M 18.935547 4.560547 L 19.814453 5.439453 L 10 15.253906 L 0.185547 5.439453 L 1.064453 4.560547 L 10 13.496094 Z" />
</Style>
<Style Selector="ScrollBar:horizontal /template/ RepeatButton.line.up /template/ Path">
<Setter Property="Data" Value="M 14.091797 19.970703 L 4.111328 10 L 14.091797 0.029297 L 14.970703 0.908203 L 5.888672 10 L 14.970703 19.091797 Z" />
</Style>
<Style Selector="ScrollBar:horizontal /template/ RepeatButton.line.down /template/ Path">
<Setter Property="Data" Value="M 5.029297 19.091797 L 14.111328 10 L 5.029297 0.908203 L 5.908203 0.029297 L 15.888672 10 L 5.908203 19.970703 Z" />
</Style>
<Style Selector="ScrollBar /template/ Rectangle#TrackRect">
<Setter Property="StrokeThickness" Value="{DynamicResource ScrollBarTrackBorderThemeThickness}" />
<Setter Property="Fill" Value="{DynamicResource ScrollBarTrackFill}" />
<Setter Property="Stroke" Value="{DynamicResource ScrollBarTrackStroke}" />
<Setter Property="Opacity" Value="0" />
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.1" />
</Transitions>
</Setter>
</Style>
<Style Selector="ScrollBar[IsExpanded=true] /template/ Rectangle#TrackRect">
<Setter Property="Fill" Value="{DynamicResource ScrollBarTrackFillPointerOver}" />
<Setter Property="Stroke" Value="{DynamicResource ScrollBarTrackStrokePointerOver}" />
<Setter Property="Opacity" Value="1" />
</Style>
<Style Selector="ScrollBar /template/ RepeatButton.largeIncrease">
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}" />
</ControlTemplate>
</Setter>
<Setter Property="Background" Value="Transparent" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Opacity" Value="0" />
</Style>
<Style Selector="ScrollBar[IsExpanded=true] /template/ RepeatButton.largeIncrease">
<Setter Property="Opacity" Value="1" />
</Style>
</Styles>

116
src/Avalonia.Themes.Fluent/ScrollViewer.xaml

@ -1,47 +1,69 @@
<Style xmlns="https://github.com/avaloniaui" Selector="ScrollViewer">
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Template">
<ControlTemplate>
<Grid ColumnDefinitions="*,Auto" RowDefinitions="*,Auto">
<ScrollContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
CanHorizontallyScroll="{TemplateBinding CanHorizontallyScroll}"
CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}"
Content="{TemplateBinding Content}"
Extent="{TemplateBinding Extent, Mode=TwoWay}"
Margin="{TemplateBinding Padding}"
Offset="{TemplateBinding Offset, Mode=TwoWay}"
Viewport="{TemplateBinding Viewport, Mode=TwoWay}">
<ScrollContentPresenter.GestureRecognizers>
<ScrollGestureRecognizer
CanHorizontallyScroll="{TemplateBinding CanHorizontallyScroll}"
CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}"
/>
</ScrollContentPresenter.GestureRecognizers>
</ScrollContentPresenter>
<ScrollBar Name="horizontalScrollBar"
Orientation="Horizontal"
LargeChange="{Binding LargeChange.Width, RelativeSource={RelativeSource TemplatedParent}}"
SmallChange="{Binding SmallChange.Width, RelativeSource={RelativeSource TemplatedParent}}"
Maximum="{TemplateBinding HorizontalScrollBarMaximum}"
Value="{TemplateBinding HorizontalScrollBarValue, Mode=TwoWay}"
ViewportSize="{TemplateBinding HorizontalScrollBarViewportSize}"
Visibility="{TemplateBinding HorizontalScrollBarVisibility}"
Grid.Row="1"
Focusable="False"/>
<ScrollBar Name="verticalScrollBar"
Orientation="Vertical"
LargeChange="{Binding LargeChange.Height, RelativeSource={RelativeSource TemplatedParent}}"
SmallChange="{Binding SmallChange.Height, RelativeSource={RelativeSource TemplatedParent}}"
Maximum="{TemplateBinding VerticalScrollBarMaximum}"
Value="{TemplateBinding VerticalScrollBarValue, Mode=TwoWay}"
ViewportSize="{TemplateBinding VerticalScrollBarViewportSize}"
Visibility="{TemplateBinding VerticalScrollBarVisibility}"
Grid.Column="1"
Focusable="False"/>
<Panel Grid.Row="1" Grid.Column="1" Background="{DynamicResource ThemeControlMidBrush}"/>
</Grid>
</ControlTemplate>
</Setter>
</Style>
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="ScrollViewer">
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Template">
<ControlTemplate>
<Grid ColumnDefinitions="*,Auto" RowDefinitions="*,Auto">
<ScrollContentPresenter Name="PART_ContentPresenter"
Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="2"
Grid.ColumnSpan="2"
Background="{TemplateBinding Background}"
CanHorizontallyScroll="{TemplateBinding CanHorizontallyScroll}"
CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}"
Content="{TemplateBinding Content}"
Extent="{TemplateBinding Extent, Mode=TwoWay}"
Margin="{TemplateBinding Padding}"
Offset="{TemplateBinding Offset, Mode=TwoWay}"
Viewport="{TemplateBinding Viewport, Mode=TwoWay}">
<ScrollContentPresenter.GestureRecognizers>
<ScrollGestureRecognizer
CanHorizontallyScroll="{TemplateBinding CanHorizontallyScroll}"
CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}" />
</ScrollContentPresenter.GestureRecognizers>
</ScrollContentPresenter>
<ScrollBar Name="PART_HorizontalScrollBar"
AllowAutoHide="{TemplateBinding AllowAutoHide}"
Orientation="Horizontal"
LargeChange="{Binding LargeChange.Width, RelativeSource={RelativeSource TemplatedParent}}"
SmallChange="{Binding SmallChange.Width, RelativeSource={RelativeSource TemplatedParent}}"
Maximum="{TemplateBinding HorizontalScrollBarMaximum}"
Value="{TemplateBinding HorizontalScrollBarValue, Mode=TwoWay}"
ViewportSize="{TemplateBinding HorizontalScrollBarViewportSize}"
Visibility="{TemplateBinding HorizontalScrollBarVisibility}"
Grid.Row="1"
Focusable="False" />
<ScrollBar Name="PART_VerticalScrollBar"
AllowAutoHide="{TemplateBinding AllowAutoHide}"
Orientation="Vertical"
LargeChange="{Binding LargeChange.Height, RelativeSource={RelativeSource TemplatedParent}}"
SmallChange="{Binding SmallChange.Height, RelativeSource={RelativeSource TemplatedParent}}"
Maximum="{TemplateBinding VerticalScrollBarMaximum}"
Value="{TemplateBinding VerticalScrollBarValue, Mode=TwoWay}"
ViewportSize="{TemplateBinding VerticalScrollBarViewportSize}"
Visibility="{TemplateBinding VerticalScrollBarVisibility}"
Grid.Column="1"
Focusable="False" />
<Panel x:Name="PART_ScrollBarsSeparator" Grid.Row="1" Grid.Column="1" Background="{DynamicResource ScrollViewerScrollBarsSeparatorBackground}" />
</Grid>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="ScrollViewer /template/ Panel#PART_ScrollBarsSeparator">
<Setter Property="Opacity" Value="0" />
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.1" />
</Transitions>
</Setter>
</Style>
<Style Selector="ScrollViewer[IsExpanded=true] /template/ Panel#PART_ScrollBarsSeparator">
<Setter Property="Opacity" Value="1" />
</Style>
</Styles>

4
src/Avalonia.Themes.Fluent/ToggleSwitch.xaml

@ -3,8 +3,8 @@
xmlns:sys="clr-namespace:System;assembly=netstandard">
<Styles.Resources>
<Thickness x:Key="ToggleSwitchTopHeaderMargin">0,0,0,6</Thickness>
<x:Double x:Key="ToggleSwitchPreContentMargin">6</x:Double>
<x:Double x:Key="ToggleSwitchPostContentMargin">6</x:Double>
<GridLength x:Key="ToggleSwitchPreContentMargin">6</GridLength>
<GridLength x:Key="ToggleSwitchPostContentMargin">6</GridLength>
<x:Double x:Key="ToggleSwitchThemeMinWidth">154</x:Double>
<x:Double x:Key="KnobOnPosition">20</x:Double>
<x:Double x:Key="KnobOffPosition">0</x:Double>

17
src/Avalonia.Themes.Fluent/TreeView.xaml

@ -1,10 +1,11 @@
<Style xmlns="https://github.com/avaloniaui" Selector="TreeView">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Style xmlns="https://github.com/avaloniaui"
Selector="TreeView">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="Template">
<ControlTemplate>
<Border BorderBrush="{TemplateBinding BorderBrush}"
@ -15,7 +16,7 @@
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"
Margin="{TemplateBinding Padding}"/>
Margin="{TemplateBinding Padding}" />
</ScrollViewer>
</Border>
</ControlTemplate>

246
src/Avalonia.Themes.Fluent/TreeViewItem.xaml

@ -1,92 +1,180 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls">
<Style Selector="TreeViewItem">
<Style.Resources>
<converters:MarginMultiplierConverter Indent="16" Left="True" x:Key="LeftMarginConverter" />
</Style.Resources>
<Setter Property="Padding" Value="2"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<ControlTemplate>
<StackPanel>
<Border Name="SelectionBorder"
Focusable="True"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
TemplatedControl.IsTemplateFocusTarget="True">
<Grid Name="PART_Header"
ColumnDefinitions="16, *"
Margin="{TemplateBinding Level, Mode=OneWay, Converter={StaticResource LeftMarginConverter}}" >
<ToggleButton Name="expander"
Focusable="False"
IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}"/>
<ContentPresenter Name="PART_HeaderPresenter"
Focusable="False"
Content="{TemplateBinding Header}"
HorizontalContentAlignment="{TemplateBinding HorizontalAlignment}"
Padding="{TemplateBinding Padding}"
Grid.Column="1"/>
</Grid>
</Border>
<ItemsPresenter Name="PART_ItemsPresenter"
IsVisible="{TemplateBinding IsExpanded}"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"/>
</StackPanel>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="TreeViewItem /template/ ToggleButton#expander">
<Setter Property="Template">
<ControlTemplate>
<Border Background="Transparent"
Width="14"
Height="12"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Path Fill="{DynamicResource ThemeForegroundBrush}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 0 2 L 4 6 L 0 10 Z"/>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Design.PreviewWith>
<Border Padding="20"
MinWidth="300">
<TreeView>
<TreeViewItem Header="Level 1">
<TreeViewItem Header="Level 2.1">
<TreeViewItem Header="Level 3.1" />
<TreeViewItem Header="Level 3.2">
<TreeViewItem Header="Level 4" />
</TreeViewItem>
</TreeViewItem>
<TreeViewItem Header="Level 2.2"
IsEnabled="False" />
</TreeViewItem>
</TreeView>
</Border>
</Design.PreviewWith>
<Style Selector="TreeViewItem /template/ ContentPresenter#PART_HeaderPresenter">
<Setter Property="Padding" Value="2"/>
</Style>
<Styles.Resources>
<x:Double x:Key="TreeViewItemIndent">16</x:Double>
<x:Double x:Key="TreeViewItemExpandCollapseChevronSize">12</x:Double>
<Thickness x:Key="TreeViewItemExpandCollapseChevronMargin">12, 0, 12, 0</Thickness>
<StreamGeometry x:Key="TreeViewItemCollapsedChevronPathData">M 1,0 10,10 l -9,10 -1,-1 L 8,10 -0,1 Z</StreamGeometry>
<StreamGeometry x:Key="TreeViewItemExpandedChevronPathData">M0,1 L10,10 20,1 19,0 10,8 1,0 Z</StreamGeometry>
<converters:MarginMultiplierConverter Indent="{StaticResource TreeViewItemIndent}"
Left="True"
x:Key="TreeViewItemLeftMarginConverter" />
</Styles.Resources>
<Style Selector="TreeViewItem /template/ Border#SelectionBorder:pointerover">
<Setter Property="Background" Value="{DynamicResource ThemeControlHighlightMidBrush}"/>
</Style>
<Style Selector="ToggleButton.ExpandCollapseChevron">
<Setter Property="Margin" Value="0" />
<Setter Property="Width" Value="{StaticResource TreeViewItemExpandCollapseChevronSize}" />
<Setter Property="Height" Value="{StaticResource TreeViewItemExpandCollapseChevronSize}" />
<Setter Property="Template">
<ControlTemplate>
<Border Background="Transparent"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Path x:Name="ChevronPath"
Fill="{DynamicResource TreeViewItemForeground}"
Stretch="Uniform"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="TreeViewItem:selected /template/ Border#SelectionBorder">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush4}"/>
</Style>
<Style Selector="TreeViewItem">
<Style.Resources />
<Setter Property="Padding" Value="0" />
<Setter Property="Background" Value="{DynamicResource TreeViewItemBackground}" />
<Setter Property="BorderBrush" Value="{DynamicResource TreeViewItemBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource TreeViewItemBorderThemeThickness}" />
<Setter Property="Foreground" Value="{DynamicResource TreeViewItemForeground}" />
<Setter Property="MinHeight" Value="{DynamicResource TreeViewItemMinHeight}" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Template">
<ControlTemplate>
<StackPanel>
<Border Name="PART_LayoutRoot"
Classes="TreeViewItemLayoutRoot"
Focusable="True"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
MinHeight="{TemplateBinding MinHeight}"
TemplatedControl.IsTemplateFocusTarget="True">
<Grid Name="PART_Header"
ColumnDefinitions="Auto, *"
Margin="{TemplateBinding Level, Mode=OneWay, Converter={StaticResource TreeViewItemLeftMarginConverter}}">
<Panel Name="PART_ExpandCollapseChevronContainer"
Margin="{StaticResource TreeViewItemExpandCollapseChevronMargin}">
<ToggleButton Name="PART_ExpandCollapseChevron"
Classes="ExpandCollapseChevron"
Focusable="False"
IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" />
</Panel>
<ContentPresenter Name="PART_HeaderPresenter"
Grid.Column="1"
Focusable="False"
Content="{TemplateBinding Header}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Margin="{TemplateBinding Padding}" />
</Grid>
</Border>
<ItemsPresenter Name="PART_ItemsPresenter"
IsVisible="{TemplateBinding IsExpanded}"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}" />
</StackPanel>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="TreeViewItem:selected /template/ Border#SelectionBorder:focus">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush3}"/>
</Style>
<!-- PointerOver state -->
<Style Selector="TreeViewItem /template/ Border#PART_LayoutRoot:pointerover">
<Setter Property="Background" Value="{DynamicResource TreeViewItemBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource TreeViewItemBorderBrushPointerOver}" />
</Style>
<Style Selector="TreeViewItem /template/ Border#PART_LayoutRoot:pointerover > ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TreeViewItemForegroundPointerOver}" />
</Style>
<Style Selector="TreeViewItem:selected /template/ Border#SelectionBorder:pointerover">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush3}"/>
</Style>
<!-- Pressed state -->
<Style Selector="TreeViewItem:pressed /template/ Border#PART_LayoutRoot:pointerover">
<Setter Property="Background" Value="{DynamicResource TreeViewItemBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource TreeViewItemBorderBrushPressed}" />
</Style>
<Style Selector="TreeViewItem:pressed /template/ Border#PART_LayoutRoot:pointerover > ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TreeViewItemForegroundPressed}" />
</Style>
<Style Selector="TreeViewItem:selected /template/ Border#SelectionBorder:pointerover:focus">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush2}"/>
</Style>
<!-- Disabled state -->
<Style Selector="TreeViewItem:disabled /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource TreeViewItemBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource TreeViewItemBorderBrushDisabled}" />
</Style>
<Style Selector="TreeViewItem:disabled /template/ Border#PART_LayoutRoot > ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TreeViewItemForegroundDisabled}" />
</Style>
<Style Selector="TreeViewItem /template/ ToggleButton#expander:checked">
<Setter Property="RenderTransform">
<RotateTransform Angle="45"/>
</Setter>
</Style>
<!-- Selected state -->
<Style Selector="TreeViewItem:selected /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource TreeViewItemBackgroundSelected}" />
<Setter Property="BorderBrush" Value="{DynamicResource TreeViewItemBorderBrushSelected}" />
</Style>
<Style Selector="TreeViewItem:selected /template/ Border#PART_LayoutRoot > ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TreeViewItemForegroundSelected}" />
</Style>
<!-- Selected PointerOver state -->
<Style Selector="TreeViewItem:selected /template/ Border#PART_LayoutRoot:pointerover">
<Setter Property="Background" Value="{DynamicResource TreeViewItemBackgroundSelectedPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource TreeViewItemBorderBrushSelectedPointerOver}" />
</Style>
<Style Selector="TreeViewItem:selected /template/ Border#PART_LayoutRoot:pointerover > ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TreeViewItemForegroundSelectedPointerOver}" />
</Style>
<!-- Selected Pressed state -->
<Style Selector="TreeViewItem:pressed:selected /template/ Border#PART_LayoutRoot:pointerover">
<Setter Property="Background" Value="{DynamicResource TreeViewItemBackgroundSelectedPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource TreeViewItemBorderBrushSelectedPressed}" />
</Style>
<Style Selector="TreeViewItem:pressed:selected /template/ Border#PART_LayoutRoot:pointerover > ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TreeViewItemForegroundSelectedPressed}" />
</Style>
<!-- Disabled Selected state -->
<Style Selector="TreeViewItem:disabled:selected /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource TreeViewItemBackgroundSelectedDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource TreeViewItemBorderBrushSelectedDisabled}" />
</Style>
<Style Selector="TreeViewItem:disabled:selected /template/ Border#PART_LayoutRoot > ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TreeViewItemForegroundSelectedDisabled}" />
</Style>
<!-- ExpandCollapseChevron Group states -->
<Style Selector="ToggleButton.ExpandCollapseChevron /template/ Path#ChevronPath">
<Setter Property="Data" Value="{StaticResource TreeViewItemCollapsedChevronPathData}" />
</Style>
<Style Selector="ToggleButton.ExpandCollapseChevron:checked /template/ Path#ChevronPath">
<Setter Property="Data" Value="{StaticResource TreeViewItemExpandedChevronPathData}" />
</Style>
<Style Selector="TreeViewItem:empty /template/ ToggleButton#PART_ExpandCollapseChevron">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="TreeViewItem:empty /template/ Panel#PART_ExpandCollapseChevronContainer">
<Setter Property="Width" Value="{StaticResource TreeViewItemExpandCollapseChevronSize}" />
</Style>
<Style Selector="TreeViewItem:empty /template/ ToggleButton#expander">
<Setter Property="IsVisible" Value="False"/>
</Style>
</Styles>

19
tests/Avalonia.Base.UnitTests/Utilities/MathUtilitiesTests.cs

@ -199,24 +199,5 @@ namespace Avalonia.Base.UnitTests.Utilities
var actual = MathUtilities.GreaterThanOrClose(1f, 1f);
Assert.True(actual);
}
[Fact]
public void Round_Layout_Value_Without_DPI_Aware()
{
const double value = 42.5;
var expectedValue = Math.Round(value);
var actualValue = MathUtilities.RoundLayoutValue(value, 1.0);
Assert.Equal(expectedValue, actualValue);
}
[Fact]
public void Round_Layout_Value_With_DPI_Aware()
{
const double dpiScale = 1.25;
const double value = 42.5;
var expectedValue = Math.Round(value * dpiScale) / dpiScale;
var actualValue = MathUtilities.RoundLayoutValue(value, dpiScale);
Assert.Equal(expectedValue, actualValue);
}
}
}

8
tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs

@ -117,13 +117,17 @@ namespace Avalonia.Controls.UnitTests.Presenters
Width = 150,
Height = 150,
},
Offset = new Vector(25, 25),
};
target.UpdateChild();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
target.Offset = new Vector(25, 25);
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
Assert.Equal(new Rect(-25, -25, 150, 150), content.Bounds);
}
@ -345,4 +349,4 @@ namespace Avalonia.Controls.UnitTests.Presenters
}
}
}
}
}

6
tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests_ILogicalScrollable.cs

@ -59,13 +59,17 @@ namespace Avalonia.Controls.UnitTests
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = scrollable,
Offset = new Vector(25, 25),
};
target.UpdateChild();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
target.Offset = new Vector(25, 25);
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
Assert.Equal(new Rect(-25, -25, 150, 150), scrollable.Bounds);
}

27
tests/Avalonia.Layout.UnitTests/LayoutHelperTests.cs

@ -0,0 +1,27 @@
using System;
using Xunit;
namespace Avalonia.Layout.UnitTests
{
public class LayoutHelperTests
{
[Fact]
public void Round_Layout_Value_Without_DPI_Aware()
{
const double value = 42.5;
var expectedValue = Math.Round(value);
var actualValue = LayoutHelper.RoundLayoutValue(value, 1.0);
Assert.Equal(expectedValue, actualValue);
}
[Fact]
public void Round_Layout_Value_With_DPI_Aware()
{
const double dpiScale = 1.25;
const double value = 42.5;
var expectedValue = Math.Round(value * dpiScale) / dpiScale;
var actualValue = LayoutHelper.RoundLayoutValue(value, dpiScale);
Assert.Equal(expectedValue, actualValue);
}
}
}
Loading…
Cancel
Save