diff --git a/build/SourceLink.props b/build/SourceLink.props
index 1e007e01eb..9f05848881 100644
--- a/build/SourceLink.props
+++ b/build/SourceLink.props
@@ -3,7 +3,6 @@
true
false
true
- embedded
$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
@@ -15,6 +14,10 @@
true
+
+ embedded
+
+
diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml
index 227b31bf20..d0e1bd885e 100644
--- a/samples/ControlCatalog/App.xaml
+++ b/samples/ControlCatalog/App.xaml
@@ -3,6 +3,7 @@
xmlns:vm="using:ControlCatalog.ViewModels"
x:DataType="vm:ApplicationViewModel"
x:CompileBindings="True"
+ Name="Avalonia ControlCatalog"
x:Class="ControlCatalog.App">
diff --git a/src/Avalonia.Themes.Default/Accents/BaseDark.xaml b/src/Avalonia.Themes.Default/Accents/BaseDark.xaml
index 5f18bac44a..1843abebfd 100644
--- a/src/Avalonia.Themes.Default/Accents/BaseDark.xaml
+++ b/src/Avalonia.Themes.Default/Accents/BaseDark.xaml
@@ -1,13 +1,9 @@
-
diff --git a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml
index 30c6d39856..6247815303 100644
--- a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml
+++ b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml
@@ -1,82 +1,38 @@
-
diff --git a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
index 678f75b43f..ef200b5532 100644
--- a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
+++ b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
@@ -13,10 +13,7 @@
-
-
-
-
+
diff --git a/src/Avalonia.Themes.Default/AutoCompleteBox.xaml b/src/Avalonia.Themes.Default/Controls/AutoCompleteBox.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/AutoCompleteBox.xaml
rename to src/Avalonia.Themes.Default/Controls/AutoCompleteBox.xaml
diff --git a/src/Avalonia.Themes.Default/Button.xaml b/src/Avalonia.Themes.Default/Controls/Button.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/Button.xaml
rename to src/Avalonia.Themes.Default/Controls/Button.xaml
diff --git a/src/Avalonia.Themes.Default/ButtonSpinner.xaml b/src/Avalonia.Themes.Default/Controls/ButtonSpinner.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/ButtonSpinner.xaml
rename to src/Avalonia.Themes.Default/Controls/ButtonSpinner.xaml
diff --git a/src/Avalonia.Themes.Default/Calendar.xaml b/src/Avalonia.Themes.Default/Controls/Calendar.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/Calendar.xaml
rename to src/Avalonia.Themes.Default/Controls/Calendar.xaml
diff --git a/src/Avalonia.Themes.Default/CalendarButton.xaml b/src/Avalonia.Themes.Default/Controls/CalendarButton.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/CalendarButton.xaml
rename to src/Avalonia.Themes.Default/Controls/CalendarButton.xaml
diff --git a/src/Avalonia.Themes.Default/CalendarDatePicker.xaml b/src/Avalonia.Themes.Default/Controls/CalendarDatePicker.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/CalendarDatePicker.xaml
rename to src/Avalonia.Themes.Default/Controls/CalendarDatePicker.xaml
diff --git a/src/Avalonia.Themes.Default/CalendarDayButton.xaml b/src/Avalonia.Themes.Default/Controls/CalendarDayButton.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/CalendarDayButton.xaml
rename to src/Avalonia.Themes.Default/Controls/CalendarDayButton.xaml
diff --git a/src/Avalonia.Themes.Default/CalendarItem.xaml b/src/Avalonia.Themes.Default/Controls/CalendarItem.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/CalendarItem.xaml
rename to src/Avalonia.Themes.Default/Controls/CalendarItem.xaml
diff --git a/src/Avalonia.Themes.Default/CaptionButtons.xaml b/src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/CaptionButtons.xaml
rename to src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml
diff --git a/src/Avalonia.Themes.Default/Carousel.xaml b/src/Avalonia.Themes.Default/Controls/Carousel.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/Carousel.xaml
rename to src/Avalonia.Themes.Default/Controls/Carousel.xaml
diff --git a/src/Avalonia.Themes.Default/CheckBox.xaml b/src/Avalonia.Themes.Default/Controls/CheckBox.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/CheckBox.xaml
rename to src/Avalonia.Themes.Default/Controls/CheckBox.xaml
diff --git a/src/Avalonia.Themes.Default/ComboBox.xaml b/src/Avalonia.Themes.Default/Controls/ComboBox.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/ComboBox.xaml
rename to src/Avalonia.Themes.Default/Controls/ComboBox.xaml
diff --git a/src/Avalonia.Themes.Default/ComboBoxItem.xaml b/src/Avalonia.Themes.Default/Controls/ComboBoxItem.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/ComboBoxItem.xaml
rename to src/Avalonia.Themes.Default/Controls/ComboBoxItem.xaml
diff --git a/src/Avalonia.Themes.Default/ContentControl.xaml b/src/Avalonia.Themes.Default/Controls/ContentControl.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/ContentControl.xaml
rename to src/Avalonia.Themes.Default/Controls/ContentControl.xaml
diff --git a/src/Avalonia.Themes.Default/ContextMenu.xaml b/src/Avalonia.Themes.Default/Controls/ContextMenu.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/ContextMenu.xaml
rename to src/Avalonia.Themes.Default/Controls/ContextMenu.xaml
diff --git a/src/Avalonia.Themes.Default/DataValidationErrors.xaml b/src/Avalonia.Themes.Default/Controls/DataValidationErrors.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/DataValidationErrors.xaml
rename to src/Avalonia.Themes.Default/Controls/DataValidationErrors.xaml
diff --git a/src/Avalonia.Themes.Default/DatePicker.xaml b/src/Avalonia.Themes.Default/Controls/DatePicker.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/DatePicker.xaml
rename to src/Avalonia.Themes.Default/Controls/DatePicker.xaml
diff --git a/src/Avalonia.Themes.Default/EmbeddableControlRoot.xaml b/src/Avalonia.Themes.Default/Controls/EmbeddableControlRoot.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/EmbeddableControlRoot.xaml
rename to src/Avalonia.Themes.Default/Controls/EmbeddableControlRoot.xaml
diff --git a/src/Avalonia.Themes.Default/Expander.xaml b/src/Avalonia.Themes.Default/Controls/Expander.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/Expander.xaml
rename to src/Avalonia.Themes.Default/Controls/Expander.xaml
diff --git a/src/Avalonia.Themes.Default/FlyoutPresenter.xaml b/src/Avalonia.Themes.Default/Controls/FlyoutPresenter.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/FlyoutPresenter.xaml
rename to src/Avalonia.Themes.Default/Controls/FlyoutPresenter.xaml
diff --git a/src/Avalonia.Themes.Default/FocusAdorner.xaml b/src/Avalonia.Themes.Default/Controls/FocusAdorner.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/FocusAdorner.xaml
rename to src/Avalonia.Themes.Default/Controls/FocusAdorner.xaml
diff --git a/src/Avalonia.Themes.Default/GridSplitter.xaml b/src/Avalonia.Themes.Default/Controls/GridSplitter.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/GridSplitter.xaml
rename to src/Avalonia.Themes.Default/Controls/GridSplitter.xaml
diff --git a/src/Avalonia.Themes.Default/ItemsControl.xaml b/src/Avalonia.Themes.Default/Controls/ItemsControl.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/ItemsControl.xaml
rename to src/Avalonia.Themes.Default/Controls/ItemsControl.xaml
diff --git a/src/Avalonia.Themes.Default/Label.xaml b/src/Avalonia.Themes.Default/Controls/Label.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/Label.xaml
rename to src/Avalonia.Themes.Default/Controls/Label.xaml
diff --git a/src/Avalonia.Themes.Default/ListBox.xaml b/src/Avalonia.Themes.Default/Controls/ListBox.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/ListBox.xaml
rename to src/Avalonia.Themes.Default/Controls/ListBox.xaml
diff --git a/src/Avalonia.Themes.Default/ListBoxItem.xaml b/src/Avalonia.Themes.Default/Controls/ListBoxItem.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/ListBoxItem.xaml
rename to src/Avalonia.Themes.Default/Controls/ListBoxItem.xaml
diff --git a/src/Avalonia.Themes.Default/ManagedFileChooser.xaml b/src/Avalonia.Themes.Default/Controls/ManagedFileChooser.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/ManagedFileChooser.xaml
rename to src/Avalonia.Themes.Default/Controls/ManagedFileChooser.xaml
diff --git a/src/Avalonia.Themes.Default/Menu.xaml b/src/Avalonia.Themes.Default/Controls/Menu.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/Menu.xaml
rename to src/Avalonia.Themes.Default/Controls/Menu.xaml
diff --git a/src/Avalonia.Themes.Default/MenuFlyoutPresenter.xaml b/src/Avalonia.Themes.Default/Controls/MenuFlyoutPresenter.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/MenuFlyoutPresenter.xaml
rename to src/Avalonia.Themes.Default/Controls/MenuFlyoutPresenter.xaml
diff --git a/src/Avalonia.Themes.Default/MenuItem.xaml b/src/Avalonia.Themes.Default/Controls/MenuItem.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/MenuItem.xaml
rename to src/Avalonia.Themes.Default/Controls/MenuItem.xaml
diff --git a/src/Avalonia.Themes.Default/NativeMenuBar.xaml b/src/Avalonia.Themes.Default/Controls/NativeMenuBar.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/NativeMenuBar.xaml
rename to src/Avalonia.Themes.Default/Controls/NativeMenuBar.xaml
diff --git a/src/Avalonia.Themes.Default/NotificationCard.xaml b/src/Avalonia.Themes.Default/Controls/NotificationCard.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/NotificationCard.xaml
rename to src/Avalonia.Themes.Default/Controls/NotificationCard.xaml
diff --git a/src/Avalonia.Themes.Default/NumericUpDown.xaml b/src/Avalonia.Themes.Default/Controls/NumericUpDown.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/NumericUpDown.xaml
rename to src/Avalonia.Themes.Default/Controls/NumericUpDown.xaml
diff --git a/src/Avalonia.Themes.Default/OverlayPopupHost.xaml b/src/Avalonia.Themes.Default/Controls/OverlayPopupHost.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/OverlayPopupHost.xaml
rename to src/Avalonia.Themes.Default/Controls/OverlayPopupHost.xaml
diff --git a/src/Avalonia.Themes.Default/PathIcon.xaml b/src/Avalonia.Themes.Default/Controls/PathIcon.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/PathIcon.xaml
rename to src/Avalonia.Themes.Default/Controls/PathIcon.xaml
diff --git a/src/Avalonia.Themes.Default/PopupRoot.xaml b/src/Avalonia.Themes.Default/Controls/PopupRoot.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/PopupRoot.xaml
rename to src/Avalonia.Themes.Default/Controls/PopupRoot.xaml
diff --git a/src/Avalonia.Themes.Default/ProgressBar.xaml b/src/Avalonia.Themes.Default/Controls/ProgressBar.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/ProgressBar.xaml
rename to src/Avalonia.Themes.Default/Controls/ProgressBar.xaml
diff --git a/src/Avalonia.Themes.Default/RadioButton.xaml b/src/Avalonia.Themes.Default/Controls/RadioButton.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/RadioButton.xaml
rename to src/Avalonia.Themes.Default/Controls/RadioButton.xaml
diff --git a/src/Avalonia.Themes.Default/RepeatButton.xaml b/src/Avalonia.Themes.Default/Controls/RepeatButton.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/RepeatButton.xaml
rename to src/Avalonia.Themes.Default/Controls/RepeatButton.xaml
diff --git a/src/Avalonia.Themes.Default/ScrollBar.xaml b/src/Avalonia.Themes.Default/Controls/ScrollBar.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/ScrollBar.xaml
rename to src/Avalonia.Themes.Default/Controls/ScrollBar.xaml
diff --git a/src/Avalonia.Themes.Default/ScrollViewer.xaml b/src/Avalonia.Themes.Default/Controls/ScrollViewer.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/ScrollViewer.xaml
rename to src/Avalonia.Themes.Default/Controls/ScrollViewer.xaml
diff --git a/src/Avalonia.Themes.Default/Separator.xaml b/src/Avalonia.Themes.Default/Controls/Separator.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/Separator.xaml
rename to src/Avalonia.Themes.Default/Controls/Separator.xaml
diff --git a/src/Avalonia.Themes.Default/Slider.xaml b/src/Avalonia.Themes.Default/Controls/Slider.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/Slider.xaml
rename to src/Avalonia.Themes.Default/Controls/Slider.xaml
diff --git a/src/Avalonia.Themes.Default/SplitView.xaml b/src/Avalonia.Themes.Default/Controls/SplitView.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/SplitView.xaml
rename to src/Avalonia.Themes.Default/Controls/SplitView.xaml
diff --git a/src/Avalonia.Themes.Default/TabControl.xaml b/src/Avalonia.Themes.Default/Controls/TabControl.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/TabControl.xaml
rename to src/Avalonia.Themes.Default/Controls/TabControl.xaml
diff --git a/src/Avalonia.Themes.Default/TabItem.xaml b/src/Avalonia.Themes.Default/Controls/TabItem.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/TabItem.xaml
rename to src/Avalonia.Themes.Default/Controls/TabItem.xaml
diff --git a/src/Avalonia.Themes.Default/TabStrip.xaml b/src/Avalonia.Themes.Default/Controls/TabStrip.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/TabStrip.xaml
rename to src/Avalonia.Themes.Default/Controls/TabStrip.xaml
diff --git a/src/Avalonia.Themes.Default/TabStripItem.xaml b/src/Avalonia.Themes.Default/Controls/TabStripItem.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/TabStripItem.xaml
rename to src/Avalonia.Themes.Default/Controls/TabStripItem.xaml
diff --git a/src/Avalonia.Themes.Default/TextBox.xaml b/src/Avalonia.Themes.Default/Controls/TextBox.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/TextBox.xaml
rename to src/Avalonia.Themes.Default/Controls/TextBox.xaml
diff --git a/src/Avalonia.Themes.Default/TimePicker.xaml b/src/Avalonia.Themes.Default/Controls/TimePicker.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/TimePicker.xaml
rename to src/Avalonia.Themes.Default/Controls/TimePicker.xaml
diff --git a/src/Avalonia.Themes.Default/TitleBar.xaml b/src/Avalonia.Themes.Default/Controls/TitleBar.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/TitleBar.xaml
rename to src/Avalonia.Themes.Default/Controls/TitleBar.xaml
diff --git a/src/Avalonia.Themes.Default/ToggleButton.xaml b/src/Avalonia.Themes.Default/Controls/ToggleButton.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/ToggleButton.xaml
rename to src/Avalonia.Themes.Default/Controls/ToggleButton.xaml
diff --git a/src/Avalonia.Themes.Default/ToggleSwitch.xaml b/src/Avalonia.Themes.Default/Controls/ToggleSwitch.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/ToggleSwitch.xaml
rename to src/Avalonia.Themes.Default/Controls/ToggleSwitch.xaml
diff --git a/src/Avalonia.Themes.Default/ToolTip.xaml b/src/Avalonia.Themes.Default/Controls/ToolTip.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/ToolTip.xaml
rename to src/Avalonia.Themes.Default/Controls/ToolTip.xaml
diff --git a/src/Avalonia.Themes.Default/TreeView.xaml b/src/Avalonia.Themes.Default/Controls/TreeView.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/TreeView.xaml
rename to src/Avalonia.Themes.Default/Controls/TreeView.xaml
diff --git a/src/Avalonia.Themes.Default/TreeViewItem.xaml b/src/Avalonia.Themes.Default/Controls/TreeViewItem.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/TreeViewItem.xaml
rename to src/Avalonia.Themes.Default/Controls/TreeViewItem.xaml
diff --git a/src/Avalonia.Themes.Default/UserControl.xaml b/src/Avalonia.Themes.Default/Controls/UserControl.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/UserControl.xaml
rename to src/Avalonia.Themes.Default/Controls/UserControl.xaml
diff --git a/src/Avalonia.Themes.Default/Window.xaml b/src/Avalonia.Themes.Default/Controls/Window.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/Window.xaml
rename to src/Avalonia.Themes.Default/Controls/Window.xaml
diff --git a/src/Avalonia.Themes.Default/WindowNotificationManager.xaml b/src/Avalonia.Themes.Default/Controls/WindowNotificationManager.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/WindowNotificationManager.xaml
rename to src/Avalonia.Themes.Default/Controls/WindowNotificationManager.xaml
diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml b/src/Avalonia.Themes.Default/DefaultTheme.xaml
index 109fde390c..702fcc7c7a 100644
--- a/src/Avalonia.Themes.Default/DefaultTheme.xaml
+++ b/src/Avalonia.Themes.Default/DefaultTheme.xaml
@@ -2,65 +2,65 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Avalonia.Themes.Default.DefaultTheme">
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml.cs b/src/Avalonia.Themes.Default/DefaultTheme.xaml.cs
index effdb88537..598b418977 100644
--- a/src/Avalonia.Themes.Default/DefaultTheme.xaml.cs
+++ b/src/Avalonia.Themes.Default/DefaultTheme.xaml.cs
@@ -1,4 +1,3 @@
-using Avalonia.Markup.Xaml;
using Avalonia.Styling;
namespace Avalonia.Themes.Default
diff --git a/src/Avalonia.Themes.Default/SimpleTheme.cs b/src/Avalonia.Themes.Default/SimpleTheme.cs
new file mode 100644
index 0000000000..1d9f2d5f9d
--- /dev/null
+++ b/src/Avalonia.Themes.Default/SimpleTheme.cs
@@ -0,0 +1,166 @@
+using System;
+using System.Collections.Generic;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Avalonia.Markup.Xaml.Styling;
+using Avalonia.Styling;
+#nullable enable
+
+namespace Avalonia.Themes.Default
+{
+ public class SimpleTheme : AvaloniaObject, IStyle, IResourceProvider
+ {
+ public static readonly StyledProperty ModeProperty =
+ AvaloniaProperty.Register(nameof(Mode));
+
+ private readonly Uri _baseUri;
+ private bool _isLoading;
+ private IStyle? _loaded;
+ private Styles _sharedStyles = new();
+ private Styles _simpleDark = new();
+ private Styles _simpleLight = new();
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The base URL for the XAML context.
+ public SimpleTheme(Uri baseUri)
+ {
+ _baseUri = baseUri;
+ InitStyles(_baseUri);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The XAML service provider.
+ public SimpleTheme(IServiceProvider serviceProvider)
+ {
+ var service = serviceProvider.GetService(typeof(IUriContext));
+ if (service == null)
+ {
+ throw new Exception("There is no service object of type IUriContext!");
+ }
+ _baseUri = ((IUriContext)service).BaseUri;
+ InitStyles(_baseUri);
+ }
+
+ public event EventHandler OwnerChanged
+ {
+ add
+ {
+ if (Loaded is IResourceProvider rp)
+ {
+ rp.OwnerChanged += value;
+ }
+ }
+ remove
+ {
+ if (Loaded is IResourceProvider rp)
+ {
+ rp.OwnerChanged -= value;
+ }
+ }
+ }
+
+ IReadOnlyList IStyle.Children => _loaded?.Children ?? Array.Empty();
+
+ bool IResourceNode.HasResources => (Loaded as IResourceProvider)?.HasResources ?? false;
+
+ public IStyle Loaded
+ {
+ get
+ {
+ if (_loaded == null)
+ {
+ _isLoading = true;
+
+ if (Mode == SimpleThemeMode.Light)
+ {
+ _loaded = new Styles { _sharedStyles, _simpleLight };
+ }
+ else if (Mode == SimpleThemeMode.Dark)
+ {
+ _loaded = new Styles { _sharedStyles, _simpleDark };
+ }
+ _isLoading = false;
+ }
+
+ return _loaded!;
+ }
+ }
+
+ ///
+ /// Gets or sets the mode of the fluent theme (light, dark).
+ ///
+ public SimpleThemeMode Mode
+ {
+ get => GetValue(ModeProperty);
+ set => SetValue(ModeProperty, value);
+ }
+ public IResourceHost? Owner => (Loaded as IResourceProvider)?.Owner;
+
+ void IResourceProvider.AddOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.AddOwner(owner);
+
+ void IResourceProvider.RemoveOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.RemoveOwner(owner);
+
+ public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) => Loaded.TryAttach(target, host);
+
+ public bool TryGetResource(object key, out object? value)
+ {
+ if (!_isLoading && Loaded is IResourceProvider p)
+ {
+ return p.TryGetResource(key, out value);
+ }
+
+ value = null;
+ return false;
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+ if (change.Property == ModeProperty)
+ {
+ if (Mode == SimpleThemeMode.Dark)
+ {
+ (Loaded as Styles)![1] = _simpleDark[0];
+ }
+ else
+ {
+ (Loaded as Styles)![1] = _simpleLight[0];
+ }
+ }
+ }
+
+ private void InitStyles(Uri baseUri)
+ {
+ _sharedStyles = new Styles
+ {
+ new StyleInclude(baseUri)
+ {
+ Source = new Uri("avares://Avalonia.Themes.Default/DefaultTheme.xaml")
+ },
+ new StyleInclude(baseUri)
+ {
+ Source = new Uri("avares://Avalonia.Themes.Default/Accents/Base.xaml")
+ }
+ };
+ _simpleLight = new Styles
+ {
+ new StyleInclude(baseUri)
+ {
+ Source = new Uri("avares://Avalonia.Themes.Default/Accents/BaseLight.xaml")
+ }
+ };
+
+ _simpleDark = new Styles
+ {
+ new StyleInclude(baseUri)
+ {
+ Source = new Uri("avares://Avalonia.Themes.Default/Accents/BaseDark.xaml")
+ }
+ };
+ }
+
+ }
+}
diff --git a/src/Avalonia.Themes.Default/SimpleThemeMode.cs b/src/Avalonia.Themes.Default/SimpleThemeMode.cs
new file mode 100644
index 0000000000..be33466327
--- /dev/null
+++ b/src/Avalonia.Themes.Default/SimpleThemeMode.cs
@@ -0,0 +1,8 @@
+namespace Avalonia.Themes.Default
+{
+ public enum SimpleThemeMode
+ {
+ Light,
+ Dark
+ }
+}
diff --git a/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml
index f2344ab380..836cc27db3 100644
--- a/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml
@@ -1,7 +1,8 @@
+ xmlns:converters="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls"
+ x:CompileBindings="True">
diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml
index ca538e4b0a..3a6af60983 100644
--- a/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml
@@ -11,7 +11,7 @@
-
+
diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml
index 26c3bbc19f..ffd3972b66 100644
--- a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml
@@ -7,7 +7,8 @@
+ xmlns:sys="clr-namespace:System;assembly=netstandard"
+ x:CompileBindings="True">
-
+
diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml
index 5bf3ac11af..a9c8281cf0 100644
--- a/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml
@@ -6,7 +6,9 @@
-->
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ x:CompileBindings="True"
+ x:DataType="CalendarItem">
@@ -32,6 +34,7 @@
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml
index 72c25cea37..831537f578 100644
--- a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml
@@ -1,7 +1,9 @@
+ xmlns:sys="clr-namespace:System;assembly=netstandard"
+ x:DataType="MenuItem"
+ x:CompileBindings="True">
diff --git a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml
index 799fe6ffe4..243095c004 100644
--- a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml
@@ -1,6 +1,7 @@
diff --git a/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml
index 3320fc9a41..9aa73fc52e 100644
--- a/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml
@@ -7,7 +7,8 @@
+ xmlns:sys="clr-namespace:System;assembly=netstandard"
+ x:CompileBindings="True">
40
1
diff --git a/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml b/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml
index debdfb2772..2d18be91cb 100644
--- a/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml
@@ -1,6 +1,7 @@
+ xmlns:sys="clr-namespace:System;assembly=netstandard"
+ x:CompileBindings="True">
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ x:DataType="WindowNotificationManager"
+ x:CompileBindings="True">
";
var styles = AvaloniaRuntimeXamlLoader.Parse(xaml);
diff --git a/tests/Avalonia.RenderTests/Media/StreamGeometryTests.cs b/tests/Avalonia.RenderTests/Media/StreamGeometryTests.cs
new file mode 100644
index 0000000000..fc9cbf6a7f
--- /dev/null
+++ b/tests/Avalonia.RenderTests/Media/StreamGeometryTests.cs
@@ -0,0 +1,56 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Avalonia.Controls;
+using Avalonia.Controls.Shapes;
+using Avalonia.Media;
+using Avalonia.Media.Imaging;
+using Xunit;
+
+#if AVALONIA_SKIA
+namespace Avalonia.Skia.RenderTests
+#else
+namespace Avalonia.Direct2D1.RenderTests.Media
+#endif
+{
+ public class StreamGeometryTests : TestBase
+ {
+ public StreamGeometryTests()
+ : base(@"Media\StreamGeometry")
+ {
+ }
+
+ [Fact]
+ public async Task PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions()
+ {
+ var grid = new Avalonia.Controls.Primitives.UniformGrid() { Columns = 2, Rows = 4, Width = 320, Height = 400 };
+ foreach (var sweepDirection in new[] { SweepDirection.Clockwise, SweepDirection.CounterClockwise })
+ foreach (var isLargeArc in new[] { false, true })
+ foreach (var isPrecise in new[] { false, true })
+ {
+ Point Pt(double x, double y) => new Point(x, y);
+ Size Sz(double w, double h) => new Size(w, h);
+ var streamGeometry = new StreamGeometry();
+ using (var context = streamGeometry.Open())
+ {
+ context.BeginFigure(Pt(20, 20), true);
+
+ if(isPrecise)
+ context.PreciseArcTo(Pt(40, 40), Sz(20, 20), 0, isLargeArc, sweepDirection);
+ else
+ context.ArcTo(Pt(40, 40), Sz(20, 20), 0, isLargeArc, sweepDirection);
+ context.LineTo(Pt(40, 20));
+ context.LineTo(Pt(20, 20));
+ context.EndFigure(true);
+ }
+ var pathShape = new Avalonia.Controls.Shapes.Path();
+ pathShape.Data = streamGeometry;
+ pathShape.Stroke = new SolidColorBrush(Colors.CornflowerBlue);
+ pathShape.Fill = new SolidColorBrush(Colors.Gold);
+ pathShape.StrokeThickness = 2;
+ pathShape.Margin = new Thickness(20);
+ grid.Children.Add(pathShape);
+ }
+ await RenderToFile(grid);
+ }
+ }
+}
diff --git a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
index d4abf9416a..f5e502bca8 100644
--- a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
+++ b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
@@ -29,4 +29,5 @@
+
diff --git a/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs b/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs
new file mode 100644
index 0000000000..002da66070
--- /dev/null
+++ b/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using Avalonia.Media;
+using Avalonia.Media.Fonts;
+using Avalonia.Platform;
+
+namespace Avalonia.UnitTests
+{
+ public class HarfBuzzFontManagerImpl : IFontManagerImpl
+ {
+ private readonly Typeface[] _customTypefaces;
+ private readonly string _defaultFamilyName;
+
+ private static readonly Typeface _defaultTypeface =
+ new Typeface("resm:Avalonia.UnitTests.Assets?assembly=Avalonia.UnitTests#Noto Mono");
+ private static readonly Typeface _italicTypeface =
+ new Typeface("resm:Avalonia.UnitTests.Assets?assembly=Avalonia.UnitTests#Noto Sans");
+ private static readonly Typeface _emojiTypeface =
+ new Typeface("resm:Avalonia.UnitTests.Assets?assembly=Avalonia.UnitTests#Twitter Color Emoji");
+
+ public HarfBuzzFontManagerImpl(string defaultFamilyName = "resm:Avalonia.UnitTests.Assets?assembly=Avalonia.UnitTests#Noto Mono")
+ {
+ _customTypefaces = new[] { _emojiTypeface, _italicTypeface, _defaultTypeface };
+ _defaultFamilyName = defaultFamilyName;
+ }
+
+ public string GetDefaultFontFamilyName()
+ {
+ return _defaultFamilyName;
+ }
+
+ public IEnumerable GetInstalledFontFamilyNames(bool checkForUpdates = false)
+ {
+ return _customTypefaces.Select(x => x.FontFamily!.Name);
+ }
+
+ public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily,
+ CultureInfo culture, out Typeface fontKey)
+ {
+ foreach (var customTypeface in _customTypefaces)
+ {
+ var glyphTypeface = customTypeface.GlyphTypeface;
+
+ if (!glyphTypeface.TryGetGlyph((uint)codepoint, out _))
+ {
+ continue;
+ }
+
+ fontKey = customTypeface;
+
+ return true;
+ }
+
+ fontKey = default;
+
+ return false;
+ }
+
+ public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface)
+ {
+ var fontFamily = typeface.FontFamily;
+
+ if (fontFamily == null)
+ {
+ return null;
+ }
+
+ if (fontFamily.IsDefault)
+ {
+ fontFamily = _defaultTypeface.FontFamily;
+ }
+
+ if (fontFamily!.Key == null)
+ {
+ return null;
+ }
+
+ var fontAssets = FontFamilyLoader.LoadFontAssets(fontFamily.Key);
+
+ var asset = fontAssets.First();
+
+ var assetLoader = AvaloniaLocator.Current.GetService();
+
+ if (assetLoader == null)
+ {
+ throw new NotSupportedException("IAssetLoader is not registered.");
+ }
+
+ var stream = assetLoader.Open(asset);
+
+ return new HarfBuzzGlyphTypefaceImpl(stream);
+ }
+ }
+}
diff --git a/tests/Avalonia.UnitTests/HarfBuzzGlyphTypefaceImpl.cs b/tests/Avalonia.UnitTests/HarfBuzzGlyphTypefaceImpl.cs
new file mode 100644
index 0000000000..32e0434cd4
--- /dev/null
+++ b/tests/Avalonia.UnitTests/HarfBuzzGlyphTypefaceImpl.cs
@@ -0,0 +1,158 @@
+using System;
+using System.IO;
+using Avalonia.Platform;
+using HarfBuzzSharp;
+
+namespace Avalonia.UnitTests
+{
+ public class HarfBuzzGlyphTypefaceImpl : IGlyphTypefaceImpl
+ {
+ private bool _isDisposed;
+ private Blob _blob;
+
+ public HarfBuzzGlyphTypefaceImpl(Stream data, bool isFakeBold = false, bool isFakeItalic = false)
+ {
+ _blob = Blob.FromStream(data);
+
+ Face = new Face(_blob, 0);
+
+ Font = new Font(Face);
+
+ Font.SetFunctionsOpenType();
+
+ Font.GetScale(out var scale, out _);
+
+ DesignEmHeight = (short)scale;
+
+ var metrics = Font.OpenTypeMetrics;
+
+ const double defaultFontRenderingEmSize = 12.0;
+
+ Ascent = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalAscender) / defaultFontRenderingEmSize * DesignEmHeight);
+
+ Descent = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalDescender) / defaultFontRenderingEmSize * DesignEmHeight);
+
+ LineGap = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalLineGap) / defaultFontRenderingEmSize * DesignEmHeight);
+
+ UnderlinePosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineOffset) / defaultFontRenderingEmSize * DesignEmHeight);
+
+ UnderlineThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineSize) / defaultFontRenderingEmSize * DesignEmHeight);
+
+ StrikethroughPosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutOffset) / defaultFontRenderingEmSize * DesignEmHeight);
+
+ StrikethroughThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutSize) / defaultFontRenderingEmSize * DesignEmHeight);
+
+ IsFixedPitch = GetGlyphAdvance(GetGlyph('a')) == GetGlyphAdvance(GetGlyph('b'));
+
+ IsFakeBold = isFakeBold;
+
+ IsFakeItalic = isFakeItalic;
+ }
+
+ public Face Face { get; }
+
+ public Font Font { get; }
+
+ ///
+ public short DesignEmHeight { get; }
+
+ ///
+ public int Ascent { get; }
+
+ ///
+ public int Descent { get; }
+
+ ///
+ public int LineGap { get; }
+
+ ///
+ public int UnderlinePosition { get; }
+
+ ///
+ public int UnderlineThickness { get; }
+
+ ///
+ public int StrikethroughPosition { get; }
+
+ ///
+ public int StrikethroughThickness { get; }
+
+ ///
+ public bool IsFixedPitch { get; }
+
+ public bool IsFakeBold { get; }
+
+ public bool IsFakeItalic { get; }
+
+ ///
+ public ushort GetGlyph(uint codepoint)
+ {
+ if (Font.TryGetGlyph(codepoint, out var glyph))
+ {
+ return (ushort)glyph;
+ }
+
+ return 0;
+ }
+
+ ///
+ public ushort[] GetGlyphs(ReadOnlySpan codepoints)
+ {
+ var glyphs = new ushort[codepoints.Length];
+
+ for (var i = 0; i < codepoints.Length; i++)
+ {
+ if (Font.TryGetGlyph(codepoints[i], out var glyph))
+ {
+ glyphs[i] = (ushort)glyph;
+ }
+ }
+
+ return glyphs;
+ }
+
+ ///
+ public int GetGlyphAdvance(ushort glyph)
+ {
+ return Font.GetHorizontalGlyphAdvance(glyph);
+ }
+
+ ///
+ public int[] GetGlyphAdvances(ReadOnlySpan glyphs)
+ {
+ var glyphIndices = new uint[glyphs.Length];
+
+ for (var i = 0; i < glyphs.Length; i++)
+ {
+ glyphIndices[i] = glyphs[i];
+ }
+
+ return Font.GetHorizontalGlyphAdvances(glyphIndices);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ if (_isDisposed)
+ {
+ return;
+ }
+
+ _isDisposed = true;
+
+ if (!disposing)
+ {
+ return;
+ }
+
+ Font?.Dispose();
+ Face?.Dispose();
+ _blob?.Dispose();
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ }
+}
diff --git a/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs b/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs
new file mode 100644
index 0000000000..687fddd71a
--- /dev/null
+++ b/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs
@@ -0,0 +1,147 @@
+using System;
+using System.Globalization;
+using Avalonia.Media;
+using Avalonia.Media.TextFormatting.Unicode;
+using Avalonia.Platform;
+using Avalonia.Utilities;
+using HarfBuzzSharp;
+using Buffer = HarfBuzzSharp.Buffer;
+
+namespace Avalonia.UnitTests
+{
+ public class HarfBuzzTextShaperImpl : ITextShaperImpl
+ {
+ public GlyphRun ShapeText(ReadOnlySlice text, Typeface typeface, double fontRenderingEmSize,
+ CultureInfo culture)
+ {
+ using (var buffer = new Buffer())
+ {
+ FillBuffer(buffer, text);
+
+ buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture);
+
+ buffer.GuessSegmentProperties();
+
+ var glyphTypeface = typeface.GlyphTypeface;
+
+ var font = ((HarfBuzzGlyphTypefaceImpl)glyphTypeface.PlatformImpl).Font;
+
+ font.Shape(buffer);
+
+ font.GetScale(out var scaleX, out _);
+
+ var textScale = fontRenderingEmSize / scaleX;
+
+ var bufferLength = buffer.Length;
+
+ var glyphInfos = buffer.GetGlyphInfoSpan();
+
+ var glyphPositions = buffer.GetGlyphPositionSpan();
+
+ var glyphIndices = new ushort[bufferLength];
+
+ var clusters = new ushort[bufferLength];
+
+ double[] glyphAdvances = null;
+
+ Vector[] glyphOffsets = null;
+
+ for (var i = 0; i < bufferLength; i++)
+ {
+ glyphIndices[i] = (ushort)glyphInfos[i].Codepoint;
+
+ clusters[i] = (ushort)glyphInfos[i].Cluster;
+
+ if (!glyphTypeface.IsFixedPitch)
+ {
+ SetAdvance(glyphPositions, i, textScale, ref glyphAdvances);
+ }
+
+ SetOffset(glyphPositions, i, textScale, ref glyphOffsets);
+ }
+
+ return new GlyphRun(glyphTypeface, fontRenderingEmSize,
+ new ReadOnlySlice(glyphIndices),
+ new ReadOnlySlice(glyphAdvances),
+ new ReadOnlySlice(glyphOffsets),
+ text,
+ new ReadOnlySlice(clusters),
+ buffer.Direction == Direction.LeftToRight ? 0 : 1);
+ }
+ }
+
+ private static void FillBuffer(Buffer buffer, ReadOnlySlice text)
+ {
+ buffer.ContentType = ContentType.Unicode;
+
+ var i = 0;
+
+ while (i < text.Length)
+ {
+ var codepoint = Codepoint.ReadAt(text, i, out var count);
+
+ var cluster = (uint)(text.Start + i);
+
+ if (codepoint.IsBreakChar)
+ {
+ if (i + 1 < text.Length)
+ {
+ var nextCodepoint = Codepoint.ReadAt(text, i + 1, out _);
+
+ if (nextCodepoint == '\n' && codepoint == '\r')
+ {
+ count++;
+
+ buffer.Add('\u200C', cluster);
+
+ buffer.Add('\u200D', cluster);
+ }
+ else
+ {
+ buffer.Add('\u200C', cluster);
+ }
+ }
+ else
+ {
+ buffer.Add('\u200C', cluster);
+ }
+ }
+ else
+ {
+ buffer.Add(codepoint, cluster);
+ }
+
+ i += count;
+ }
+ }
+
+ private static void SetOffset(ReadOnlySpan glyphPositions, int index, double textScale,
+ ref Vector[] offsetBuffer)
+ {
+ var position = glyphPositions[index];
+
+ if (position.XOffset == 0 && position.YOffset == 0)
+ {
+ return;
+ }
+
+ offsetBuffer ??= new Vector[glyphPositions.Length];
+
+ var offsetX = position.XOffset * textScale;
+
+ var offsetY = position.YOffset * textScale;
+
+ offsetBuffer[index] = new Vector(offsetX, offsetY);
+ }
+
+ private static void SetAdvance(ReadOnlySpan glyphPositions, int index, double textScale,
+ ref double[] advanceBuffer)
+ {
+ advanceBuffer ??= new double[glyphPositions.Length];
+
+ // Depends on direction of layout
+ // advanceBuffer[index] = buffer.GlyphPositions[index].YAdvance * textScale;
+ advanceBuffer[index] = glyphPositions[index].XAdvance * textScale;
+ }
+ }
+}
diff --git a/tests/Avalonia.UnitTests/TestServices.cs b/tests/Avalonia.UnitTests/TestServices.cs
index 8d27562146..da678fd74b 100644
--- a/tests/Avalonia.UnitTests/TestServices.cs
+++ b/tests/Avalonia.UnitTests/TestServices.cs
@@ -58,6 +58,12 @@ namespace Avalonia.UnitTests
public static readonly TestServices RealStyler = new TestServices(
styler: new Styler());
+ public static readonly TestServices TextServices = new TestServices(
+ assetLoader: new AssetLoader(),
+ renderInterface: new MockPlatformRenderInterface(),
+ fontManagerImpl: new HarfBuzzFontManagerImpl(),
+ textShaperImpl: new HarfBuzzTextShaperImpl());
+
public TestServices(
IAssetLoader assetLoader = null,
IFocusManager focusManager = null,
@@ -168,7 +174,7 @@ namespace Avalonia.UnitTests
};
var baseLight = (IStyle)AvaloniaXamlLoader.Load(
- new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"));
+ new Uri("avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"));
result.Add(baseLight);
return result;
diff --git a/tests/Avalonia.UnitTests/UnitTestApplication.cs b/tests/Avalonia.UnitTests/UnitTestApplication.cs
index 2d37057779..dc10754ea2 100644
--- a/tests/Avalonia.UnitTests/UnitTestApplication.cs
+++ b/tests/Avalonia.UnitTests/UnitTestApplication.cs
@@ -10,6 +10,7 @@ using System.Reactive.Disposables;
using System.Reactive.Concurrency;
using Avalonia.Input.Platform;
using Avalonia.Animation;
+using Avalonia.Shared.PlatformSupport;
namespace Avalonia.UnitTests
{
@@ -29,6 +30,11 @@ namespace Avalonia.UnitTests
RegisterServices();
}
+ static UnitTestApplication()
+ {
+ AssetLoader.RegisterResUriParsers();
+ }
+
public static new UnitTestApplication Current => (UnitTestApplication)Application.Current;
public TestServices Services => _services;
diff --git a/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs
index 6e5b8eb637..a48639a426 100644
--- a/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs
+++ b/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs
@@ -48,7 +48,16 @@ namespace Avalonia.Visuals.UnitTests.Media
[Fact]
public void Should_Use_FontManagerOptions_FontFallback()
{
- var options = new FontManagerOptions { FontFallbacks = new[] { new FontFallback { FontFamily = new FontFamily("MyFont"), UnicodeRange = UnicodeRange.Default} } };
+ var options = new FontManagerOptions
+ {
+ FontFallbacks = new[]
+ {
+ new FontFallback
+ {
+ FontFamily = new FontFamily("MyFont"), UnicodeRange = UnicodeRange.Default
+ }
+ }
+ };
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface
.With(fontManagerImpl: new MockFontManagerImpl())))
diff --git a/tests/Avalonia.Visuals.UnitTests/Media/GlyphRunTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/GlyphRunTests.cs
index 58feb4714a..f52bdc39c8 100644
--- a/tests/Avalonia.Visuals.UnitTests/Media/GlyphRunTests.cs
+++ b/tests/Avalonia.Visuals.UnitTests/Media/GlyphRunTests.cs
@@ -22,6 +22,7 @@ namespace Avalonia.Visuals.UnitTests.Media
[Theory]
public void Should_Get_Distance_From_CharacterHit(double[] advances, ushort[] clusters, int start, int trailingLength, double expectedDistance)
{
+ using(UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
using (var glyphRun = CreateGlyphRun(advances, clusters))
{
var characterHit = new CharacterHit(start, trailingLength);
@@ -40,6 +41,7 @@ namespace Avalonia.Visuals.UnitTests.Media
public void Should_Get_CharacterHit_FromDistance(double[] advances, ushort[] clusters, double distance, int start,
int trailingLengthExpected, bool isInsideExpected)
{
+ using(UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
using (var glyphRun = CreateGlyphRun(advances, clusters))
{
var textBounds = glyphRun.GetCharacterHitFromDistance(distance, out var isInside);
@@ -63,6 +65,7 @@ namespace Avalonia.Visuals.UnitTests.Media
public void Should_Find_Nearest_CharacterHit(double[] advances, ushort[] clusters, int bidiLevel,
int index, int expectedIndex, int expectedLength, double expectedWidth)
{
+ using(UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
using (var glyphRun = CreateGlyphRun(advances, clusters, bidiLevel))
{
var textBounds = glyphRun.FindNearestCharacterHit(index, out var width);
@@ -87,6 +90,7 @@ namespace Avalonia.Visuals.UnitTests.Media
int nextIndex, int nextLength,
int bidiLevel)
{
+ using(UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
using (var glyphRun = CreateGlyphRun(advances, clusters, bidiLevel))
{
var characterHit = glyphRun.GetNextCaretCharacterHit(new CharacterHit(currentIndex, currentLength));
@@ -109,6 +113,7 @@ namespace Avalonia.Visuals.UnitTests.Media
int previousIndex, int previousLength,
int bidiLevel)
{
+ using(UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
using (var glyphRun = CreateGlyphRun(advances, clusters, bidiLevel))
{
var characterHit = glyphRun.GetPreviousCaretCharacterHit(new CharacterHit(currentIndex, currentLength));
@@ -128,6 +133,7 @@ namespace Avalonia.Visuals.UnitTests.Media
[Theory]
public void Should_Find_Glyph_Index(double[] advances, ushort[] clusters, int bidiLevel)
{
+ using(UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
using (var glyphRun = CreateGlyphRun(advances, clusters, bidiLevel))
{
if (glyphRun.IsLeftToRight)
diff --git a/tests/TestFiles/Direct2D1/Media/StreamGeometry/PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions.deferred.expected.png b/tests/TestFiles/Direct2D1/Media/StreamGeometry/PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions.deferred.expected.png
new file mode 100644
index 0000000000..825b1d7ea7
Binary files /dev/null and b/tests/TestFiles/Direct2D1/Media/StreamGeometry/PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions.deferred.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/StreamGeometry/PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions.immediate.expected.png b/tests/TestFiles/Direct2D1/Media/StreamGeometry/PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions.immediate.expected.png
new file mode 100644
index 0000000000..825b1d7ea7
Binary files /dev/null and b/tests/TestFiles/Direct2D1/Media/StreamGeometry/PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions.immediate.expected.png differ
diff --git a/tests/TestFiles/Skia/Media/StreamGeometry/PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions.deferred.expected.png b/tests/TestFiles/Skia/Media/StreamGeometry/PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions.deferred.expected.png
new file mode 100644
index 0000000000..f7cc90678a
Binary files /dev/null and b/tests/TestFiles/Skia/Media/StreamGeometry/PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions.deferred.expected.png differ
diff --git a/tests/TestFiles/Skia/Media/StreamGeometry/PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions.immediate.expected.png b/tests/TestFiles/Skia/Media/StreamGeometry/PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions.immediate.expected.png
new file mode 100644
index 0000000000..f7cc90678a
Binary files /dev/null and b/tests/TestFiles/Skia/Media/StreamGeometry/PreciseEllipticArc_Produces_Valid_Arcs_In_All_Directions.immediate.expected.png differ