diff --git a/.gitmodules b/.gitmodules
index 9dbc50ef61..2d11fdfa9e 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -2,5 +2,5 @@
path = nukebuild/Numerge
url = https://github.com/kekekeks/Numerge.git
[submodule "src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github"]
- path = src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
+ path = src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
url = https://github.com/kekekeks/XamlX.git
diff --git a/Avalonia.sln b/Avalonia.sln
index 4ab647a25e..4954260e12 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -211,6 +211,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless", "src\Av
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.Vnc", "src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj", "{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Markup.Xaml.Loader", "src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj", "{909A8CBD-7D0E-42FD-B841-022AD8925820}"
+EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@@ -1998,6 +2000,30 @@ Global
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhone.Build.0 = Release|Any CPU
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|Any CPU.Build.0 = Release|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhone.Build.0 = Release|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2056,6 +2082,7 @@ Global
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{351337F5-D66F-461B-A957-4EF60BDB4BA6} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
+ {909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}
diff --git a/Directory.Build.props b/Directory.Build.props
index 1f26df9bbc..b41f8c488e 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,5 +1,6 @@
$(MSBuildThisFileDirectory)build-intermediate/nuget
+ $(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll
diff --git a/dirs.proj b/dirs.proj
index 4b3b1183f0..26c8f54b23 100644
--- a/dirs.proj
+++ b/dirs.proj
@@ -6,7 +6,7 @@
-
+
diff --git a/native/Avalonia.Native/src/OSX/Screens.mm b/native/Avalonia.Native/src/OSX/Screens.mm
index 278daf9a18..455cfa2e41 100644
--- a/native/Avalonia.Native/src/OSX/Screens.mm
+++ b/native/Avalonia.Native/src/OSX/Screens.mm
@@ -4,6 +4,14 @@ class Screens : public ComSingleObject
{
public:
FORWARD_IUNKNOWN()
+
+ private:
+ CGFloat PrimaryDisplayHeight()
+ {
+ return NSMaxY([[[NSScreen screens] firstObject] frame]);
+ }
+
+public:
virtual HRESULT GetScreenCount (int* ret) override
{
@autoreleasepool
@@ -25,15 +33,15 @@ class Screens : public ComSingleObject
auto screen = [[NSScreen screens] objectAtIndex:index];
- ret->Bounds.X = [screen frame].origin.x;
- ret->Bounds.Y = [screen frame].origin.y;
ret->Bounds.Height = [screen frame].size.height;
ret->Bounds.Width = [screen frame].size.width;
+ ret->Bounds.X = [screen frame].origin.x;
+ ret->Bounds.Y = PrimaryDisplayHeight() - [screen frame].origin.y - ret->Bounds.Height;
- ret->WorkingArea.X = [screen visibleFrame].origin.x;
- ret->WorkingArea.Y = [screen visibleFrame].origin.y;
ret->WorkingArea.Height = [screen visibleFrame].size.height;
ret->WorkingArea.Width = [screen visibleFrame].size.width;
+ ret->WorkingArea.X = [screen visibleFrame].origin.x;
+ ret->WorkingArea.Y = ret->Bounds.Height - [screen visibleFrame].origin.y - ret->WorkingArea.Height;
ret->PixelDensity = [screen backingScaleFactor];
diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
index 69fd2a9f13..71dce93ce7 100644
--- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
+++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
@@ -126,7 +126,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_view.Visibility = ViewStates.Visible;
}
- public double Scaling => 1;
+ public double RenderScaling => 1;
void Draw()
{
diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
index 7117fd51a2..490364c0d8 100644
--- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
+++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
@@ -17,14 +17,14 @@
Shared/AvaloniaResourceXamlInfo.cs
-
+
XamlIlExtensions/%(RecursiveDir)%(FileName)%(Extension)
-
+
XamlIl/%(RecursiveDir)%(FileName)%(Extension)
-
+
XamlIl.Cecil/%(RecursiveDir)%(FileName)%(Extension)
@@ -57,8 +57,8 @@
Markup/%(RecursiveDir)%(FileName)%(Extension)
-
-
+
+
diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs
index fe9656a00b..7e921944ea 100644
--- a/src/Avalonia.Controls.DataGrid/DataGrid.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs
@@ -3922,6 +3922,12 @@ namespace Avalonia.Controls
dataGridCell: editingCell);
EditingRow.InvalidateDesiredHeight();
+ var column = editingCell.OwningColumn;
+ if (column.Width.IsSizeToCells || column.Width.IsAuto)
+ {// Invalidate desired width and force recalculation
+ column.SetWidthDesiredValue(0);
+ EditingRow.OwningGrid.AutoSizeColumn(column, editingCell.DesiredSize.Width);
+ }
}
// We're done, so raise the CellEditEnded event
diff --git a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
index 5d3311e8c6..a41c159980 100644
--- a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
+++ b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
@@ -88,7 +88,7 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterDirect(nameof(SelectedDate),
x => x.SelectedDate, (x, v) => x.SelectedDate = v);
- //Template Items
+ // Template Items
private Button _flyoutButton;
private TextBlock _dayText;
private TextBlock _monthText;
@@ -359,10 +359,14 @@ namespace Avalonia.Controls
}
}
- Grid.SetColumn(_spacer1, 1);
- Grid.SetColumn(_spacer2, 3);
- _spacer1.IsVisible = columnIndex > 1;
- _spacer2.IsVisible = columnIndex > 2;
+ var isSpacer1Visible = columnIndex > 1;
+ var isSpacer2Visible = columnIndex > 2;
+ // ternary conditional operator is used to make sure grid cells will be validated
+ Grid.SetColumn(_spacer1, isSpacer1Visible ? 1 : 0);
+ Grid.SetColumn(_spacer2, isSpacer2Visible ? 3 : 0);
+
+ _spacer1.IsVisible = isSpacer1Visible;
+ _spacer2.IsVisible = isSpacer2Visible;
}
private void SetSelectedDateText()
@@ -398,7 +402,7 @@ namespace Avalonia.Controls
var deltaY = _presenter.GetOffsetForPopup();
- //The extra 5 px I think is related to default popup placement behavior
+ // The extra 5 px I think is related to default popup placement behavior
_popup.Host.ConfigurePosition(_popup.PlacementTarget, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
diff --git a/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
index 8b86e46e88..31527ccb16 100644
--- a/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
+++ b/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
@@ -77,7 +77,7 @@ namespace Avalonia.Controls
DatePicker.YearVisibleProperty.AddOwner(x =>
x.YearVisible, (x, v) => x.YearVisible = v);
- //Template Items
+ // Template Items
private Grid _pickerContainer;
private Button _acceptButton;
private Button _dismissButton;
@@ -107,7 +107,7 @@ namespace Avalonia.Controls
private bool _yearVisible = true;
private DateTimeOffset _syncDate;
- private GregorianCalendar _calendar;
+ private readonly GregorianCalendar _calendar;
private bool _suppressUpdateSelection;
public DatePickerPresenter()
@@ -234,7 +234,7 @@ namespace Avalonia.Controls
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
- //These are requirements, so throw if not found
+ // These are requirements, so throw if not found
_pickerContainer = e.NameScope.Get("PickerContainer");
_monthHost = e.NameScope.Get("MonthHost");
_dayHost = e.NameScope.Get("DayHost");
@@ -326,7 +326,7 @@ namespace Avalonia.Controls
///
private void InitPicker()
{
- //OnApplyTemplate must've been called before we can init here...
+ // OnApplyTemplate must've been called before we can init here...
if (_pickerContainer == null)
return;
@@ -344,12 +344,11 @@ namespace Avalonia.Controls
SetGrid();
- //Date should've been set when we reach this point
+ // Date should've been set when we reach this point
var dt = Date;
if (DayVisible)
{
- GregorianCalendar gc = new GregorianCalendar();
- var maxDays = gc.GetDaysInMonth(dt.Year, dt.Month);
+ var maxDays = _calendar.GetDaysInMonth(dt.Year, dt.Month);
_daySelector.MaximumValue = maxDays;
_daySelector.MinimumValue = 1;
_daySelector.SelectedValue = dt.Day;
@@ -407,10 +406,14 @@ namespace Avalonia.Controls
}
}
- Grid.SetColumn(_spacer1, 1);
- Grid.SetColumn(_spacer2, 3);
- _spacer1.IsVisible = columnIndex > 1;
- _spacer2.IsVisible = columnIndex > 2;
+ var isSpacer1Visible = columnIndex > 1;
+ var isSpacer2Visible = columnIndex > 2;
+ // ternary conditional operator is used to make sure grid cells will be validated
+ Grid.SetColumn(_spacer1, isSpacer1Visible ? 1 : 0);
+ Grid.SetColumn(_spacer2, isSpacer2Visible ? 3 : 0);
+
+ _spacer1.IsVisible = isSpacer1Visible;
+ _spacer2.IsVisible = isSpacer2Visible;
}
private void SetInitialFocus()
@@ -433,12 +436,12 @@ namespace Avalonia.Controls
}
}
- private void OnDismissButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
+ private void OnDismissButtonClicked(object sender, RoutedEventArgs e)
{
OnDismiss();
}
- private void OnAcceptButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
+ private void OnAcceptButtonClicked(object sender, RoutedEventArgs e)
{
Date = _syncDate;
OnConfirmed();
@@ -471,7 +474,7 @@ namespace Avalonia.Controls
_syncDate = newDate;
- //We don't need to update the days if not displaying day, not february
+ // We don't need to update the days if not displaying day, not february
if (!DayVisible || _syncDate.Month != 2)
return;
diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
index f8bd2878d9..522103c7bd 100644
--- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
+++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
@@ -35,7 +35,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
}
}
- public double Scaling
+ public double RenderScaling
{
get { return _scaling; }
set
diff --git a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
index cd1ce3deae..9e65ef5f81 100644
--- a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
@@ -142,7 +142,6 @@ namespace Avalonia.Controls.Generators
private readonly IDataTemplate _inner;
public WrapperTreeDataTemplate(IDataTemplate inner) => _inner = inner;
public IControl Build(object param) => _inner.Build(param);
- public bool SupportsRecycling => _inner.SupportsRecycling;
public bool Match(object data) => _inner.Match(data);
public InstancedBinding ItemsSelector(object item) => null;
}
diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
index 0d77cbf802..7514f214aa 100644
--- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
+++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
@@ -23,10 +23,10 @@ namespace Avalonia.Platform
Size ClientSize { get; }
///
- /// Gets the scaling factor for the toplevel.
+ /// Gets the scaling factor for the toplevel. This is used for rendering.
///
- double Scaling { get; }
-
+ double RenderScaling { get; }
+
///
/// The list of native platform's surfaces that can be consumed by rendering subsystems.
///
diff --git a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
index b190c4f2e7..ecaf87d1ed 100644
--- a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
+++ b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
@@ -13,6 +13,11 @@ namespace Avalonia.Platform
/// Hides the window.
///
void Hide();
+
+ ///
+ /// Gets the scaling factor for Window positioning and sizing.
+ ///
+ double DesktopScaling { get; }
///
/// Gets the position of the window in device pixels.
diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs
index c4571505ba..8837901816 100644
--- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs
@@ -86,7 +86,7 @@ namespace Avalonia.Controls.Presenters
private IControl _child;
private bool _createdChild;
- private IDataTemplate _dataTemplate;
+ private IRecyclingDataTemplate _recyclingDataTemplate;
private readonly BorderRenderHelper _borderRenderer = new BorderRenderHelper();
///
@@ -281,7 +281,7 @@ namespace Avalonia.Controls.Presenters
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
- _dataTemplate = null;
+ _recyclingDataTemplate = null;
_createdChild = false;
InvalidateMeasure();
}
@@ -307,22 +307,21 @@ namespace Avalonia.Controls.Presenters
{
var dataTemplate = this.FindDataTemplate(content, ContentTemplate) ?? FuncDataTemplate.Default;
- // We have content and it isn't a control, so if the new data template is the same
- // as the old data template, try to recycle the existing child control to display
- // the new data.
- if (dataTemplate == _dataTemplate && dataTemplate.SupportsRecycling)
+ if (dataTemplate is IRecyclingDataTemplate rdt)
{
- newChild = oldChild;
+ var toRecycle = rdt == _recyclingDataTemplate ? oldChild : null;
+ newChild = rdt.Build(content, toRecycle);
+ _recyclingDataTemplate = rdt;
}
else
{
- _dataTemplate = dataTemplate;
- newChild = _dataTemplate.Build(content);
+ newChild = dataTemplate.Build(content);
+ _recyclingDataTemplate = null;
}
}
else
{
- _dataTemplate = null;
+ _recyclingDataTemplate = null;
}
return newChild;
@@ -422,7 +421,7 @@ namespace Avalonia.Controls.Presenters
LogicalChildren.Remove(Child);
((ISetInheritanceParent)Child).SetParent(Child.Parent);
Child = null;
- _dataTemplate = null;
+ _recyclingDataTemplate = null;
}
InvalidateMeasure();
diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs
index b0e3d1ab08..91ed5d975d 100644
--- a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs
+++ b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs
@@ -40,9 +40,9 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
public void MoveAndResize(Point devicePoint, Size virtualSize)
{
- _moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, _parent.Scaling);
+ _moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, _parent.RenderScaling);
}
- public virtual double Scaling => _parent.Scaling;
+ public virtual double Scaling => _parent.DesktopScaling;
}
}
diff --git a/src/Avalonia.Controls/Repeater/ElementFactory.cs b/src/Avalonia.Controls/Repeater/ElementFactory.cs
index 1c1b71af88..644e077221 100644
--- a/src/Avalonia.Controls/Repeater/ElementFactory.cs
+++ b/src/Avalonia.Controls/Repeater/ElementFactory.cs
@@ -4,8 +4,6 @@ namespace Avalonia.Controls
{
public abstract class ElementFactory : IElementFactory
{
- bool IDataTemplate.SupportsRecycling => false;
-
public IControl Build(object data)
{
return GetElementCore(new ElementFactoryGetArgs { Data = data });
diff --git a/src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs b/src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs
index 4b784375a9..dd97cde218 100644
--- a/src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs
+++ b/src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs
@@ -13,7 +13,6 @@ namespace Avalonia.Controls
public ItemTemplateWrapper(IDataTemplate dataTemplate) => _dataTemplate = dataTemplate;
- public bool SupportsRecycling => false;
public IControl Build(object param) => GetElement(null, param);
public bool Match(object data) => _dataTemplate.Match(data);
diff --git a/src/Avalonia.Controls/Templates/FuncDataTemplate.cs b/src/Avalonia.Controls/Templates/FuncDataTemplate.cs
index d454a29021..1afd86a11e 100644
--- a/src/Avalonia.Controls/Templates/FuncDataTemplate.cs
+++ b/src/Avalonia.Controls/Templates/FuncDataTemplate.cs
@@ -6,7 +6,7 @@ namespace Avalonia.Controls.Templates
///
/// Builds a control for a piece of data.
///
- public class FuncDataTemplate : FuncTemplate
+
+
+
+
+
+
diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs
index 052ee5e1b7..16b4f90d57 100644
--- a/src/Avalonia.Visuals/Media/Color.cs
+++ b/src/Avalonia.Visuals/Media/Color.cs
@@ -89,6 +89,11 @@ namespace Avalonia.Media
/// The .
public static Color Parse(string s)
{
+ if (s is null)
+ {
+ throw new ArgumentNullException(nameof(s));
+ }
+
if (TryParse(s, out Color color))
{
return color;
@@ -120,14 +125,16 @@ namespace Avalonia.Media
/// The status of the operation.
public static bool TryParse(string s, out Color color)
{
- if (s == null)
+ color = default;
+
+ if (s is null)
{
- throw new ArgumentNullException(nameof(s));
+ return false;
}
if (s.Length == 0)
{
- throw new FormatException();
+ return false;
}
if (s[0] == '#' && TryParseInternal(s.AsSpan(), out color))
@@ -144,8 +151,6 @@ namespace Avalonia.Media
return true;
}
- color = default;
-
return false;
}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/GenericTextParagraphProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/GenericTextParagraphProperties.cs
index c4302aecec..8e7d934bca 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/GenericTextParagraphProperties.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/GenericTextParagraphProperties.cs
@@ -4,14 +4,12 @@
{
private TextAlignment _textAlignment;
private TextWrapping _textWrapping;
- private TextTrimming _textTrimming;
private double _lineHeight;
public GenericTextParagraphProperties(
TextRunProperties defaultTextRunProperties,
TextAlignment textAlignment = TextAlignment.Left,
- TextWrapping textWrapping = TextWrapping.WrapWithOverflow,
- TextTrimming textTrimming = TextTrimming.None,
+ TextWrapping textWrapping = TextWrapping.NoWrap,
double lineHeight = 0)
{
DefaultTextRunProperties = defaultTextRunProperties;
@@ -20,8 +18,6 @@
_textWrapping = textWrapping;
- _textTrimming = textTrimming;
-
_lineHeight = lineHeight;
}
@@ -31,8 +27,6 @@
public override TextWrapping TextWrapping => _textWrapping;
- public override TextTrimming TextTrimming => _textTrimming;
-
public override double LineHeight => _lineHeight;
///
@@ -50,13 +44,6 @@
{
_textWrapping = textWrapping;
}
- ///
- /// Set text trimming
- ///
- internal void SetTextTrimming(TextTrimming textTrimming)
- {
- _textTrimming = textTrimming;
- }
///
/// Set line height
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs b/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs
index b71fe5bc3c..9e67a03f45 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs
@@ -1,5 +1,4 @@
-using Avalonia.Media.TextFormatting.Unicode;
-using Avalonia.Utilities;
+using Avalonia.Utilities;
namespace Avalonia.Media.TextFormatting
{
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs
new file mode 100644
index 0000000000..ffd65423a3
--- /dev/null
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs
@@ -0,0 +1,23 @@
+namespace Avalonia.Media.TextFormatting
+{
+ ///
+ /// Properties of text collapsing
+ ///
+ public abstract class TextCollapsingProperties
+ {
+ ///
+ /// Gets the width in which the collapsible range is constrained to
+ ///
+ public abstract double Width { get; }
+
+ ///
+ /// Gets the text run that is used as collapsing symbol
+ ///
+ public abstract TextRun Symbol { get; }
+
+ ///
+ /// Gets the style of collapsing
+ ///
+ public abstract TextCollapsingStyle Style { get; }
+ }
+}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingStyle.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingStyle.cs
new file mode 100644
index 0000000000..1523cc4d9a
--- /dev/null
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingStyle.cs
@@ -0,0 +1,18 @@
+namespace Avalonia.Media.TextFormatting
+{
+ ///
+ /// Text collapsing style
+ ///
+ public enum TextCollapsingStyle
+ {
+ ///
+ /// Collapse trailing characters
+ ///
+ TrailingCharacter,
+
+ ///
+ /// Collapse trailing words
+ ///
+ TrailingWord,
+ }
+}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
index 3ad23f3504..9318fcc68e 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
@@ -1,52 +1,194 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using Avalonia.Media.TextFormatting.Unicode;
-using Avalonia.Platform;
-using Avalonia.Utilities;
namespace Avalonia.Media.TextFormatting
{
internal class TextFormatterImpl : TextFormatter
{
- private static readonly ReadOnlySlice s_ellipsis = new ReadOnlySlice(new[] { '\u2026' });
-
///
public override TextLine FormatLine(ITextSource textSource, int firstTextSourceIndex, double paragraphWidth,
TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak = null)
{
- var textTrimming = paragraphProperties.TextTrimming;
var textWrapping = paragraphProperties.TextWrapping;
- TextLine textLine = null;
var textRuns = FetchTextRuns(textSource, firstTextSourceIndex, previousLineBreak, out var nextLineBreak);
var textRange = GetTextRange(textRuns);
- if (textTrimming != TextTrimming.None)
+ TextLine textLine;
+
+ switch (textWrapping)
+ {
+ case TextWrapping.NoWrap:
+ {
+ var textLineMetrics =
+ TextLineMetrics.Create(textRuns, textRange, paragraphWidth, paragraphProperties);
+
+ textLine = new TextLineImpl(textRuns, textLineMetrics, nextLineBreak);
+ break;
+ }
+ case TextWrapping.WrapWithOverflow:
+ case TextWrapping.Wrap:
+ {
+ textLine = PerformTextWrapping(textRuns, textRange, paragraphWidth, paragraphProperties);
+ break;
+ }
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ return textLine;
+ }
+
+ ///
+ /// Measures the number of characters that fits into available width.
+ ///
+ /// The text run.
+ /// The available width.
+ ///
+ internal static int MeasureCharacters(ShapedTextCharacters textCharacters, double availableWidth)
+ {
+ var glyphRun = textCharacters.GlyphRun;
+
+ if (glyphRun.Bounds.Width < availableWidth)
{
- textLine = PerformTextTrimming(textRuns, textRange, paragraphWidth, paragraphProperties);
+ return glyphRun.Characters.Length;
+ }
+
+ var glyphCount = 0;
+
+ var currentWidth = 0.0;
+
+ if (glyphRun.GlyphAdvances.IsEmpty)
+ {
+ var glyphTypeface = glyphRun.GlyphTypeface;
+
+ for (var i = 0; i < glyphRun.GlyphClusters.Length; i++)
+ {
+ var glyph = glyphRun.GlyphIndices[i];
+
+ var advance = glyphTypeface.GetGlyphAdvance(glyph) * glyphRun.Scale;
+
+ if (currentWidth + advance > availableWidth)
+ {
+ break;
+ }
+
+ currentWidth += advance;
+
+ glyphCount++;
+ }
}
else
{
- switch (textWrapping)
+ foreach (var advance in glyphRun.GlyphAdvances)
{
- case TextWrapping.NoWrap:
- {
- var textLineMetrics =
- TextLineMetrics.Create(textRuns, textRange, paragraphWidth, paragraphProperties);
+ if (currentWidth + advance > availableWidth)
+ {
+ break;
+ }
- textLine = new TextLineImpl(textRuns, textLineMetrics, nextLineBreak);
- break;
+ currentWidth += advance;
+
+ glyphCount++;
+ }
+ }
+
+ if (glyphCount == glyphRun.GlyphIndices.Length)
+ {
+ return glyphRun.Characters.Length;
+ }
+
+ if (glyphRun.GlyphClusters.IsEmpty)
+ {
+ return glyphCount;
+ }
+
+ var firstCluster = glyphRun.GlyphClusters[0];
+
+ var lastCluster = glyphRun.GlyphClusters[glyphCount];
+
+ return lastCluster - firstCluster;
+ }
+
+ ///
+ /// Split a sequence of runs into two segments at specified length.
+ ///
+ /// The text run's.
+ /// The length to split at.
+ /// The split text runs.
+ internal static SplitTextRunsResult SplitTextRuns(IReadOnlyList textRuns, int length)
+ {
+ var currentLength = 0;
+
+ for (var i = 0; i < textRuns.Count; i++)
+ {
+ var currentRun = textRuns[i];
+
+ if (currentLength + currentRun.GlyphRun.Characters.Length < length)
+ {
+ currentLength += currentRun.GlyphRun.Characters.Length;
+ continue;
+ }
+
+ var firstCount = currentRun.GlyphRun.Characters.Length >= 1 ? i + 1 : i;
+
+ var first = new ShapedTextCharacters[firstCount];
+
+ if (firstCount > 1)
+ {
+ for (var j = 0; j < i; j++)
+ {
+ first[j] = textRuns[j];
+ }
+ }
+
+ var secondCount = textRuns.Count - firstCount;
+
+ if (currentLength + currentRun.GlyphRun.Characters.Length == length)
+ {
+ var second = new ShapedTextCharacters[secondCount];
+
+ var offset = currentRun.GlyphRun.Characters.Length > 1 ? 1 : 0;
+
+ if (secondCount > 0)
+ {
+ for (var j = 0; j < secondCount; j++)
+ {
+ second[j] = textRuns[i + j + offset];
}
- case TextWrapping.WrapWithOverflow:
- case TextWrapping.Wrap:
+ }
+
+ first[i] = currentRun;
+
+ return new SplitTextRunsResult(first, second);
+ }
+ else
+ {
+ secondCount++;
+
+ var second = new ShapedTextCharacters[secondCount];
+
+ if (secondCount > 0)
+ {
+ for (var j = 1; j < secondCount; j++)
{
- textLine = PerformTextWrapping(textRuns, textRange, paragraphWidth, paragraphProperties);
- break;
+ second[j] = textRuns[i + j];
}
+ }
+
+ var split = currentRun.Split(length - currentLength);
+
+ first[i] = split.First;
+
+ second[0] = split.Second;
+
+ return new SplitTextRunsResult(first, second);
}
}
- return textLine;
+ return new SplitTextRunsResult(textRuns, null);
}
///
@@ -174,87 +316,6 @@ namespace Avalonia.Media.TextFormatting
return false;
}
- ///
- /// Performs text trimming and returns a trimmed line.
- ///
- /// The text runs to perform the trimming on.
- /// The text range that is covered by the text runs.
- /// A value that specifies the width of the paragraph that the line fills.
- /// A value that represents paragraph properties,
- /// such as TextWrapping, TextAlignment, or TextStyle.
- ///
- private static TextLine PerformTextTrimming(IReadOnlyList textRuns, TextRange textRange,
- double paragraphWidth, TextParagraphProperties paragraphProperties)
- {
- var textTrimming = paragraphProperties.TextTrimming;
- var availableWidth = paragraphWidth;
- var currentWidth = 0.0;
- var runIndex = 0;
-
- while (runIndex < textRuns.Count)
- {
- var currentRun = textRuns[runIndex];
-
- currentWidth += currentRun.GlyphRun.Bounds.Width;
-
- if (currentWidth > availableWidth)
- {
- var ellipsisRun = CreateEllipsisRun(currentRun.Properties);
-
- var measuredLength = MeasureText(currentRun, availableWidth - ellipsisRun.GlyphRun.Bounds.Width);
-
- if (textTrimming == TextTrimming.WordEllipsis)
- {
- if (measuredLength < textRange.End)
- {
- var currentBreakPosition = 0;
-
- var lineBreaker = new LineBreakEnumerator(currentRun.Text);
-
- while (currentBreakPosition < measuredLength && lineBreaker.MoveNext())
- {
- var nextBreakPosition = lineBreaker.Current.PositionWrap;
-
- if (nextBreakPosition == 0)
- {
- break;
- }
-
- if (nextBreakPosition > measuredLength)
- {
- break;
- }
-
- currentBreakPosition = nextBreakPosition;
- }
-
- measuredLength = currentBreakPosition;
- }
- }
-
- var splitResult = SplitTextRuns(textRuns, measuredLength);
-
- var trimmedRuns = new List(splitResult.First.Count + 1);
-
- trimmedRuns.AddRange(splitResult.First);
-
- trimmedRuns.Add(ellipsisRun);
-
- var textLineMetrics =
- TextLineMetrics.Create(trimmedRuns, textRange, paragraphWidth, paragraphProperties);
-
- return new TextLineImpl(trimmedRuns, textLineMetrics);
- }
-
- availableWidth -= currentRun.GlyphRun.Bounds.Width;
-
- runIndex++;
- }
-
- return new TextLineImpl(textRuns,
- TextLineMetrics.Create(textRuns, textRange, paragraphWidth, paragraphProperties));
- }
-
///
/// Performs text wrapping returns a list of text lines.
///
@@ -269,7 +330,7 @@ namespace Avalonia.Media.TextFormatting
var availableWidth = paragraphWidth;
var currentWidth = 0.0;
var runIndex = 0;
- var length = 0;
+ var currentLength = 0;
while (runIndex < textRuns.Count)
{
@@ -277,60 +338,55 @@ namespace Avalonia.Media.TextFormatting
if (currentWidth + currentRun.GlyphRun.Bounds.Width > availableWidth)
{
- var measuredLength = MeasureText(currentRun, paragraphWidth - currentWidth);
+ var measuredLength = MeasureCharacters(currentRun, paragraphWidth - currentWidth);
+
+ var breakFound = false;
+
+ var currentBreakPosition = 0;
if (measuredLength < currentRun.Text.Length)
{
- if (paragraphProperties.TextWrapping == TextWrapping.WrapWithOverflow)
- {
- var lineBreaker = new LineBreakEnumerator(currentRun.Text.Skip(measuredLength));
+ var lineBreaker = new LineBreakEnumerator(currentRun.Text);
- if (lineBreaker.MoveNext())
- {
- measuredLength += lineBreaker.Current.PositionWrap;
- }
- else
- {
- measuredLength = currentRun.Text.Length;
- }
- }
- else
+ while (currentBreakPosition < measuredLength && lineBreaker.MoveNext())
{
- var currentBreakPosition = -1;
+ var nextBreakPosition = lineBreaker.Current.PositionWrap;
- var lineBreaker = new LineBreakEnumerator(currentRun.Text);
-
- while (currentBreakPosition < measuredLength && lineBreaker.MoveNext())
+ if (nextBreakPosition == 0 || nextBreakPosition > measuredLength)
{
- var nextBreakPosition = lineBreaker.Current.PositionWrap;
+ break;
+ }
- if (nextBreakPosition == 0)
- {
- break;
- }
+ breakFound = lineBreaker.Current.Required ||
+ lineBreaker.Current.PositionWrap != currentRun.Text.Length;
- if (nextBreakPosition > measuredLength)
- {
- break;
- }
+ currentBreakPosition = nextBreakPosition;
+ }
+ }
- currentBreakPosition = nextBreakPosition;
- }
+ if (breakFound)
+ {
+ measuredLength = currentBreakPosition;
+ }
+ else
+ {
+ if (paragraphProperties.TextWrapping == TextWrapping.WrapWithOverflow)
+ {
+ var lineBreaker = new LineBreakEnumerator(currentRun.Text.Skip(currentBreakPosition));
- if (currentBreakPosition != -1)
+ if (lineBreaker.MoveNext())
{
- measuredLength = currentBreakPosition;
+ measuredLength = currentBreakPosition + lineBreaker.Current.PositionWrap;
}
-
}
}
- length += measuredLength;
+ currentLength += measuredLength;
- var splitResult = SplitTextRuns(textRuns, length);
+ var splitResult = SplitTextRuns(textRuns, currentLength);
var textLineMetrics = TextLineMetrics.Create(splitResult.First,
- new TextRange(textRange.Start, length), paragraphWidth, paragraphProperties);
+ new TextRange(textRange.Start, currentLength), paragraphWidth, paragraphProperties);
var lineBreak = splitResult.Second != null && splitResult.Second.Count > 0 ?
new TextLineBreak(splitResult.Second) :
@@ -341,7 +397,7 @@ namespace Avalonia.Media.TextFormatting
currentWidth += currentRun.GlyphRun.Bounds.Width;
- length += currentRun.GlyphRun.Characters.Length;
+ currentLength += currentRun.GlyphRun.Characters.Length;
runIndex++;
}
@@ -350,94 +406,6 @@ namespace Avalonia.Media.TextFormatting
TextLineMetrics.Create(textRuns, textRange, paragraphWidth, paragraphProperties));
}
- ///
- /// Measures the number of characters that fits into available width.
- ///
- /// The text run.
- /// The available width.
- ///
- private static int MeasureText(ShapedTextCharacters textCharacters, double availableWidth)
- {
- var glyphRun = textCharacters.GlyphRun;
-
- if (glyphRun.Bounds.Width < availableWidth)
- {
- return glyphRun.Characters.Length;
- }
-
- var glyphCount = 0;
-
- var currentWidth = 0.0;
-
- if (glyphRun.GlyphAdvances.IsEmpty)
- {
- var glyphTypeface = glyphRun.GlyphTypeface;
-
- for (var i = 0; i < glyphRun.GlyphClusters.Length; i++)
- {
- var glyph = glyphRun.GlyphIndices[i];
-
- var advance = glyphTypeface.GetGlyphAdvance(glyph) * glyphRun.Scale;
-
- if (currentWidth + advance > availableWidth)
- {
- break;
- }
-
- currentWidth += advance;
-
- glyphCount++;
- }
- }
- else
- {
- for (var i = 0; i < glyphRun.GlyphAdvances.Length; i++)
- {
- var advance = glyphRun.GlyphAdvances[i];
-
- if (currentWidth + advance > availableWidth)
- {
- break;
- }
-
- currentWidth += advance;
-
- glyphCount++;
- }
- }
-
- if (glyphCount == glyphRun.GlyphIndices.Length)
- {
- return glyphRun.Characters.Length;
- }
-
- if (glyphRun.GlyphClusters.IsEmpty)
- {
- return glyphCount;
- }
-
- var firstCluster = glyphRun.GlyphClusters[0];
-
- var lastCluster = glyphRun.GlyphClusters[glyphCount];
-
- return lastCluster - firstCluster;
- }
-
- ///
- /// Creates an ellipsis.
- ///
- /// The text run properties.
- ///
- private static ShapedTextCharacters CreateEllipsisRun(TextRunProperties properties)
- {
- var formatterImpl = AvaloniaLocator.Current.GetService();
-
- var glyphRun = formatterImpl.ShapeText(s_ellipsis, properties.Typeface, properties.FontRenderingEmSize,
- properties.CultureInfo);
-
- return new ShapedTextCharacters(glyphRun, properties);
- }
-
///
/// Gets the text range that is covered by the text runs.
///
@@ -464,86 +432,7 @@ namespace Avalonia.Media.TextFormatting
return new TextRange(start, end - start);
}
- ///
- /// Split a sequence of runs into two segments at specified length.
- ///
- /// The text run's.
- /// The length to split at.
- /// The split text runs.
- private static SplitTextRunsResult SplitTextRuns(IReadOnlyList textRuns, int length)
- {
- var currentLength = 0;
-
- for (var i = 0; i < textRuns.Count; i++)
- {
- var currentRun = textRuns[i];
-
- if (currentLength + currentRun.GlyphRun.Characters.Length < length)
- {
- currentLength += currentRun.GlyphRun.Characters.Length;
- continue;
- }
-
- var firstCount = currentRun.GlyphRun.Characters.Length >= 1 ? i + 1 : i;
-
- var first = new ShapedTextCharacters[firstCount];
-
- if (firstCount > 1)
- {
- for (var j = 0; j < i; j++)
- {
- first[j] = textRuns[j];
- }
- }
-
- var secondCount = textRuns.Count - firstCount;
-
- if (currentLength + currentRun.GlyphRun.Characters.Length == length)
- {
- var second = new ShapedTextCharacters[secondCount];
-
- var offset = currentRun.GlyphRun.Characters.Length > 1 ? 1 : 0;
-
- if (secondCount > 0)
- {
- for (var j = 0; j < secondCount; j++)
- {
- second[j] = textRuns[i + j + offset];
- }
- }
-
- first[i] = currentRun;
-
- return new SplitTextRunsResult(first, second);
- }
- else
- {
- secondCount++;
-
- var second = new ShapedTextCharacters[secondCount];
-
- if (secondCount > 0)
- {
- for (var j = 1; j < secondCount; j++)
- {
- second[j] = textRuns[i + j];
- }
- }
-
- var split = currentRun.Split(length - currentLength);
-
- first[i] = split.First;
-
- second[0] = split.Second;
-
- return new SplitTextRunsResult(first, second);
- }
- }
-
- return new SplitTextRunsResult(textRuns, null);
- }
-
- private readonly struct SplitTextRunsResult
+ internal readonly struct SplitTextRunsResult
{
public SplitTextRunsResult(IReadOnlyList first, IReadOnlyList second)
{
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
index 54745144c8..14602a2560 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
@@ -1,9 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Utilities;
-using Avalonia.Platform;
namespace Avalonia.Media.TextFormatting
{
@@ -17,6 +15,7 @@ namespace Avalonia.Media.TextFormatting
private readonly ReadOnlySlice _text;
private readonly TextParagraphProperties _paragraphProperties;
private readonly IReadOnlyList> _textStyleOverrides;
+ private readonly TextTrimming _textTrimming;
///
/// Initializes a new instance of the class.
@@ -54,9 +53,11 @@ namespace Avalonia.Media.TextFormatting
new ReadOnlySlice(text.AsMemory());
_paragraphProperties =
- CreateTextParagraphProperties(typeface, fontSize, foreground, textAlignment, textWrapping, textTrimming,
+ CreateTextParagraphProperties(typeface, fontSize, foreground, textAlignment, textWrapping,
textDecorations, lineHeight);
+ _textTrimming = textTrimming;
+
_textStyleOverrides = textStyleOverrides;
LineHeight = lineHeight;
@@ -143,18 +144,16 @@ namespace Avalonia.Media.TextFormatting
/// The foreground.
/// The text alignment.
/// The text wrapping.
- /// The text trimming.
/// The text decorations.
/// The height of each line of text.
///
private static TextParagraphProperties CreateTextParagraphProperties(Typeface typeface, double fontSize,
- IBrush foreground, TextAlignment textAlignment, TextWrapping textWrapping, TextTrimming textTrimming,
+ IBrush foreground, TextAlignment textAlignment, TextWrapping textWrapping,
TextDecorationCollection textDecorations, double lineHeight)
{
var textRunStyle = new GenericTextRunProperties(typeface, fontSize, textDecorations, foreground);
- return new GenericTextParagraphProperties(textRunStyle, textAlignment, textWrapping, textTrimming,
- lineHeight);
+ return new GenericTextParagraphProperties(textRunStyle, textAlignment, textWrapping, lineHeight);
}
///
@@ -214,25 +213,44 @@ namespace Avalonia.Media.TextFormatting
var textSource = new FormattedTextSource(_text,
_paragraphProperties.DefaultTextRunProperties, _textStyleOverrides);
- TextLineBreak previousLineBreak = null;
+ TextLine previousLine = null;
- while (currentPosition < _text.Length && (MaxLines == 0 || textLines.Count < MaxLines))
+ while (currentPosition < _text.Length)
{
var textLine = TextFormatter.Current.FormatLine(textSource, currentPosition, MaxWidth,
- _paragraphProperties, previousLineBreak);
+ _paragraphProperties, previousLine?.LineBreak);
- previousLineBreak = textLine.LineBreak;
+ currentPosition += textLine.TextRange.Length;
- textLines.Add(textLine);
+ if (textLines.Count > 0)
+ {
+ if (textLines.Count == MaxLines || !double.IsPositiveInfinity(MaxHeight) &&
+ height + textLine.LineMetrics.Size.Height > MaxHeight)
+ {
+ if (previousLine?.LineBreak != null && _textTrimming != TextTrimming.None)
+ {
+ var collapsedLine =
+ previousLine.Collapse(GetCollapsingProperties(MaxWidth));
- UpdateBounds(textLine, ref width, ref height);
+ textLines[textLines.Count - 1] = collapsedLine;
+ }
+
+ break;
+ }
+ }
+
+ var hasOverflowed = textLine.LineMetrics.HasOverflowed;
- if (!double.IsPositiveInfinity(MaxHeight) && height > MaxHeight)
+ if (hasOverflowed && _textTrimming != TextTrimming.None)
{
- break;
+ textLine = textLine.Collapse(GetCollapsingProperties(MaxWidth));
}
- currentPosition += textLine.TextRange.Length;
+ textLines.Add(textLine);
+
+ UpdateBounds(textLine, ref width, ref height);
+
+ previousLine = textLine;
if (currentPosition != _text.Length || textLine.LineBreak == null)
{
@@ -250,6 +268,23 @@ namespace Avalonia.Media.TextFormatting
}
}
+ ///
+ /// Gets the for current text trimming mode.
+ ///
+ /// The collapsing width.
+ /// The .
+ private TextCollapsingProperties GetCollapsingProperties(double width)
+ {
+ return _textTrimming switch
+ {
+ TextTrimming.CharacterEllipsis => new TextTrailingCharacterEllipsis(width,
+ _paragraphProperties.DefaultTextRunProperties),
+ TextTrimming.WordEllipsis => new TextTrailingWordEllipsis(width,
+ _paragraphProperties.DefaultTextRunProperties),
+ _ => throw new ArgumentOutOfRangeException(),
+ };
+ }
+
private readonly struct FormattedTextSource : ITextSource
{
private readonly ReadOnlySlice _text;
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs
index c3b7dfc77a..423ca9fb7f 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs
@@ -39,6 +39,14 @@ namespace Avalonia.Media.TextFormatting
///
public abstract TextLineBreak LineBreak { get; }
+ ///
+ /// Gets a value that indicates whether the line is collapsed.
+ ///
+ ///
+ /// true, if the line is collapsed; otherwise, false.
+ ///
+ public abstract bool HasCollapsed { get; }
+
///
/// Draws the at the given origin.
///
@@ -47,40 +55,49 @@ namespace Avalonia.Media.TextFormatting
public abstract void Draw(DrawingContext drawingContext, Point origin);
///
- /// Client to get the character hit corresponding to the specified
- /// distance from the beginning of the line.
+ /// Create a collapsed line based on collapsed text properties.
+ ///
+ /// A list of
+ /// objects that represent the collapsed text properties.
+ ///
+ /// A value that represents a collapsed line that can be displayed.
+ ///
+ public abstract TextLine Collapse(params TextCollapsingProperties[] collapsingPropertiesList);
+
+ ///
+ /// Gets the character hit corresponding to the specified distance from the beginning of the line.
///
- /// distance in text flow direction from the beginning of the line
- /// The
+ /// A value that represents the distance from the beginning of the line.
+ /// The object at the specified distance from the beginning of the line.
public abstract CharacterHit GetCharacterHitFromDistance(double distance);
///
- /// Client to get the distance from the beginning of the line from the specified
+ /// Gets the distance from the beginning of the line to the specified character hit.
/// .
///
- /// of the character to query the distance.
- /// Distance in text flow direction from the beginning of the line.
+ /// The object whose distance you want to query.
+ /// A that represents the distance from the beginning of the line.
public abstract double GetDistanceFromCharacterHit(CharacterHit characterHit);
///
- /// Client to get the next for caret navigation.
+ /// Gets the next character hit for caret navigation.
///
/// The current .
/// The next .
public abstract CharacterHit GetNextCaretCharacterHit(CharacterHit characterHit);
///
- /// Client to get the previous character hit for caret navigation
+ /// Gets the previous character hit for caret navigation.
///
- /// the current character hit
- /// The previous
+ /// The current .
+ /// The previous .
public abstract CharacterHit GetPreviousCaretCharacterHit(CharacterHit characterHit);
///
- /// Client to get the previous character hit after backspacing
+ /// Gets the previous character hit after backspacing.
///
- /// the current character hit
- /// The after backspacing
+ /// The current .
+ /// The after backspacing.
public abstract CharacterHit GetBackspaceCaretCharacterHit(CharacterHit characterHit);
///
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
index a1a9b50793..f73a7be759 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
@@ -1,4 +1,6 @@
using System.Collections.Generic;
+using Avalonia.Media.TextFormatting.Unicode;
+using Avalonia.Platform;
namespace Avalonia.Media.TextFormatting
{
@@ -7,11 +9,12 @@ namespace Avalonia.Media.TextFormatting
private readonly IReadOnlyList _textRuns;
public TextLineImpl(IReadOnlyList textRuns, TextLineMetrics lineMetrics,
- TextLineBreak lineBreak = null)
+ TextLineBreak lineBreak = null, bool hasCollapsed = false)
{
_textRuns = textRuns;
LineMetrics = lineMetrics;
LineBreak = lineBreak;
+ HasCollapsed = hasCollapsed;
}
///
@@ -26,6 +29,9 @@ namespace Avalonia.Media.TextFormatting
///
public override TextLineBreak LineBreak { get; }
+ ///
+ public override bool HasCollapsed { get; }
+
///
public override void Draw(DrawingContext drawingContext, Point origin)
{
@@ -41,6 +47,99 @@ namespace Avalonia.Media.TextFormatting
}
}
+ ///
+ public override TextLine Collapse(params TextCollapsingProperties[] collapsingPropertiesList)
+ {
+ if (collapsingPropertiesList == null || collapsingPropertiesList.Length == 0)
+ {
+ return this;
+ }
+
+ var collapsingProperties = collapsingPropertiesList[0];
+ var runIndex = 0;
+ var currentWidth = 0.0;
+ var textRange = TextRange;
+ var collapsedLength = 0;
+ TextLineMetrics textLineMetrics;
+
+ var shapedSymbol = CreateShapedSymbol(collapsingProperties.Symbol);
+
+ var availableWidth = collapsingProperties.Width - shapedSymbol.Bounds.Width;
+
+ while (runIndex < _textRuns.Count)
+ {
+ var currentRun = _textRuns[runIndex];
+
+ currentWidth += currentRun.GlyphRun.Bounds.Width;
+
+ if (currentWidth > availableWidth)
+ {
+ var measuredLength = TextFormatterImpl.MeasureCharacters(currentRun, availableWidth);
+
+ var currentBreakPosition = 0;
+
+ if (measuredLength < textRange.End)
+ {
+ var lineBreaker = new LineBreakEnumerator(currentRun.Text);
+
+ while (currentBreakPosition < measuredLength && lineBreaker.MoveNext())
+ {
+ var nextBreakPosition = lineBreaker.Current.PositionWrap;
+
+ if (nextBreakPosition == 0)
+ {
+ break;
+ }
+
+ if (nextBreakPosition > measuredLength)
+ {
+ break;
+ }
+
+ currentBreakPosition = nextBreakPosition;
+ }
+ }
+
+ if (collapsingProperties.Style == TextCollapsingStyle.TrailingWord)
+ {
+ measuredLength = currentBreakPosition;
+ }
+
+ collapsedLength += measuredLength;
+
+ var splitResult = TextFormatterImpl.SplitTextRuns(_textRuns, collapsedLength);
+
+ var shapedTextCharacters = new List(splitResult.First.Count + 1);
+
+ shapedTextCharacters.AddRange(splitResult.First);
+
+ shapedTextCharacters.Add(shapedSymbol);
+
+ textRange = new TextRange(textRange.Start, collapsedLength);
+
+ var shapedWidth = GetShapedWidth(shapedTextCharacters);
+
+ textLineMetrics = new TextLineMetrics(new Size(shapedWidth, LineMetrics.Size.Height),
+ LineMetrics.TextBaseline, textRange, false);
+
+ return new TextLineImpl(shapedTextCharacters, textLineMetrics, LineBreak, true);
+ }
+
+ availableWidth -= currentRun.GlyphRun.Bounds.Width;
+
+ collapsedLength += currentRun.GlyphRun.Characters.Length;
+
+ runIndex++;
+ }
+
+ textLineMetrics =
+ new TextLineMetrics(LineMetrics.Size.WithWidth(LineMetrics.Size.Width + shapedSymbol.Bounds.Width),
+ LineMetrics.TextBaseline, TextRange, LineMetrics.HasOverflowed);
+
+ return new TextLineImpl(new List(_textRuns) { shapedSymbol }, textLineMetrics, null,
+ true);
+ }
+
///
public override CharacterHit GetCharacterHitFromDistance(double distance)
{
@@ -82,7 +181,7 @@ namespace Avalonia.Media.TextFormatting
return nextCharacterHit;
}
- return new CharacterHit(TextRange.End); // Can't move, we're after the last character
+ return characterHit; // Can't move, we're after the last character
}
///
@@ -93,7 +192,7 @@ namespace Avalonia.Media.TextFormatting
return previousCharacterHit;
}
- return new CharacterHit(TextRange.Start); // Can't move, we're before the first character
+ return characterHit; // Can't move, we're before the first character
}
///
@@ -148,9 +247,13 @@ namespace Avalonia.Media.TextFormatting
{
var run = _textRuns[runIndex];
- nextCharacterHit = run.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex + characterHit.TrailingLength, out _);
+ var foundCharacterHit = run.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex + characterHit.TrailingLength, out _);
- if (codepointIndex <= nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength)
+ nextCharacterHit = characterHit.TrailingLength != 0 ?
+ foundCharacterHit :
+ new CharacterHit(foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength);
+
+ if (nextCharacterHit.FirstCharacterIndex > characterHit.FirstCharacterIndex)
{
return true;
}
@@ -184,9 +287,13 @@ namespace Avalonia.Media.TextFormatting
{
var run = _textRuns[runIndex];
- previousCharacterHit = run.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex - 1, out _);
+ var foundCharacterHit = run.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex - 1, out _);
+
+ previousCharacterHit = characterHit.TrailingLength != 0 ?
+ foundCharacterHit :
+ new CharacterHit(foundCharacterHit.FirstCharacterIndex);
- if (previousCharacterHit.FirstCharacterIndex < codepointIndex)
+ if (previousCharacterHit.FirstCharacterIndex < characterHit.FirstCharacterIndex)
{
return true;
}
@@ -230,5 +337,41 @@ namespace Avalonia.Media.TextFormatting
return runIndex;
}
+
+ ///
+ /// Creates a shaped symbol.
+ ///
+ /// The symbol run to shape.
+ ///
+ /// The shaped symbol.
+ ///
+ internal static ShapedTextCharacters CreateShapedSymbol(TextRun textRun)
+ {
+ var formatterImpl = AvaloniaLocator.Current.GetService();
+
+ var glyphRun = formatterImpl.ShapeText(textRun.Text, textRun.Properties.Typeface, textRun.Properties.FontRenderingEmSize,
+ textRun.Properties.CultureInfo);
+
+ return new ShapedTextCharacters(glyphRun, textRun.Properties);
+ }
+
+ ///
+ /// Gets the shaped width of specified shaped text characters.
+ ///
+ /// The shaped text characters.
+ ///
+ /// The shaped width.
+ ///
+ private static double GetShapedWidth(IReadOnlyList shapedTextCharacters)
+ {
+ var shapedWidth = 0.0;
+
+ for (var i = 0; i < shapedTextCharacters.Count; i++)
+ {
+ shapedWidth += shapedTextCharacters[i].Bounds.Width;
+ }
+
+ return shapedWidth;
+ }
}
}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs
index 2f7809ff35..6875cc1c04 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs
@@ -9,11 +9,12 @@ namespace Avalonia.Media.TextFormatting
///
public readonly struct TextLineMetrics
{
- public TextLineMetrics(Size size, double textBaseline, TextRange textRange)
+ public TextLineMetrics(Size size, double textBaseline, TextRange textRange, bool hasOverflowed)
{
Size = size;
TextBaseline = textBaseline;
TextRange = textRange;
+ HasOverflowed = hasOverflowed;
}
///
@@ -37,6 +38,12 @@ namespace Avalonia.Media.TextFormatting
///
public double TextBaseline { get; }
+ ///
+ /// Gets a boolean value that indicates whether content of the line overflows
+ /// the specified paragraph width.
+ ///
+ public bool HasOverflowed { get; }
+
///
/// Creates the text line metrics.
///
@@ -83,7 +90,7 @@ namespace Avalonia.Media.TextFormatting
descent - ascent + lineGap :
paragraphProperties.LineHeight);
- return new TextLineMetrics(size, -ascent, textRange);
+ return new TextLineMetrics(size, -ascent, textRange, size.Width > paragraphWidth);
}
}
}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs
index 39eb695404..3ecd1aafd9 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs
@@ -26,11 +26,6 @@
///
public abstract TextWrapping TextWrapping { get; }
- ///
- /// Gets the text trimming.
- ///
- public abstract TextTrimming TextTrimming { get; }
-
///
/// Paragraph's line height
///
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs
index bbcdfe2d8e..c4f9443c3d 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs
@@ -4,12 +4,11 @@ using System.Globalization;
namespace Avalonia.Media.TextFormatting
{
///
- /// Properties that can change from one run to the next, such as typeface or foreground brush.
+ /// Provides a set of properties, such as typeface or foreground brush, that can be applied to a TextRun object. This is an abstract class.
///
///
- /// The client provides a concrete implementation of this abstract run properties class. This
- /// allows client to implement their run properties the way that fits with their run formatting
- /// store.
+ /// The text layout client provides a concrete implementation of this abstract class.
+ /// This enables the client to implement text run properties in a way that corresponds with the associated formatting store.
///
public abstract class TextRunProperties : IEquatable
{
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs
new file mode 100644
index 0000000000..4bd46e8c75
--- /dev/null
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs
@@ -0,0 +1,33 @@
+using Avalonia.Utilities;
+
+namespace Avalonia.Media.TextFormatting
+{
+ ///
+ /// a collapsing properties to collapse whole line toward the end
+ /// at character granularity and with ellipsis being the collapsing symbol
+ ///
+ public class TextTrailingCharacterEllipsis : TextCollapsingProperties
+ {
+ private static readonly ReadOnlySlice s_ellipsis = new ReadOnlySlice(new[] { '\u2026' });
+
+ ///
+ /// Construct a text trailing character ellipsis collapsing properties
+ ///
+ /// width in which collapsing is constrained to
+ /// text run properties of ellispis symbol
+ public TextTrailingCharacterEllipsis(double width, TextRunProperties textRunProperties)
+ {
+ Width = width;
+ Symbol = new TextCharacters(s_ellipsis, textRunProperties);
+ }
+
+ ///
+ public sealed override double Width { get; }
+
+ ///
+ public sealed override TextRun Symbol { get; }
+
+ ///
+ public sealed override TextCollapsingStyle Style { get; } = TextCollapsingStyle.TrailingCharacter;
+ }
+}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs
new file mode 100644
index 0000000000..9dffddd207
--- /dev/null
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs
@@ -0,0 +1,37 @@
+using Avalonia.Utilities;
+
+namespace Avalonia.Media.TextFormatting
+{
+ ///
+ /// a collapsing properties to collapse whole line toward the end
+ /// at word granularity and with ellipsis being the collapsing symbol
+ ///
+ public class TextTrailingWordEllipsis : TextCollapsingProperties
+ {
+ private static readonly ReadOnlySlice s_ellipsis = new ReadOnlySlice(new[] { '\u2026' });
+
+ ///
+ /// Construct a text trailing word ellipsis collapsing properties
+ ///
+ /// width in which collapsing is constrained to
+ /// text run properties of ellispis symbol
+ public TextTrailingWordEllipsis(
+ double width,
+ TextRunProperties textRunProperties
+ )
+ {
+ Width = width;
+ Symbol = new TextCharacters(s_ellipsis, textRunProperties);
+ }
+
+
+ ///
+ public sealed override double Width { get; }
+
+ ///
+ public sealed override TextRun Symbol { get; }
+
+ ///
+ public sealed override TextCollapsingStyle Style { get; } = TextCollapsingStyle.TrailingWord;
+ }
+}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/Codepoint.cs b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/Codepoint.cs
index 20fe345d93..2f46fdd9d0 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/Codepoint.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/Codepoint.cs
@@ -112,7 +112,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
{
count = 1;
- if (index > text.End)
+ if (index > text.Length)
{
return ReplacementCodepoint;
}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs
index 26f7721128..76bb9ac44f 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs
@@ -109,7 +109,6 @@ namespace Avalonia.Media.TextFormatting.Unicode
{
case PairBreakType.DI: // Direct break
shouldBreak = true;
- _lastPos = _pos;
break;
case PairBreakType.IN: // possible indirect break
diff --git a/src/Avalonia.Visuals/Media/TextWrapping.cs b/src/Avalonia.Visuals/Media/TextWrapping.cs
index d649bda23f..b7915e5612 100644
--- a/src/Avalonia.Visuals/Media/TextWrapping.cs
+++ b/src/Avalonia.Visuals/Media/TextWrapping.cs
@@ -5,13 +5,6 @@ namespace Avalonia.Media
///
public enum TextWrapping
{
- ///
- /// Line-breaking occurs if the line overflows the available block width.
- /// However, a line may overflow the block width if the line breaking algorithm
- /// cannot determine a break opportunity, as in the case of a very long word.
- ///
- WrapWithOverflow,
-
///
/// Text should not wrap.
///
@@ -20,6 +13,13 @@ namespace Avalonia.Media
///
/// Text can wrap.
///
- Wrap
+ Wrap,
+
+ ///
+ /// Line-breaking occurs if the line overflows the available block width.
+ /// However, a line may overflow the block width if the line breaking algorithm
+ /// cannot determine a break opportunity, as in the case of a very long word.
+ ///
+ WrapWithOverflow
}
}
diff --git a/src/Avalonia.X11/X11NativeControlHost.cs b/src/Avalonia.X11/X11NativeControlHost.cs
index 23fb27f72b..6c4eb81c84 100644
--- a/src/Avalonia.X11/X11NativeControlHost.cs
+++ b/src/Avalonia.X11/X11NativeControlHost.cs
@@ -167,7 +167,7 @@ namespace Avalonia.X11
XUnmapWindow(_display, _holder.Handle);
}
- size *= _attachedTo.Window.Scaling;
+ size *= _attachedTo.Window.RenderScaling;
XResizeWindow(_display, _child.Handle,
Math.Max(1, (int)size.Width), Math.Max(1, (int)size.Height));
}
@@ -179,7 +179,7 @@ namespace Avalonia.X11
CheckDisposed();
if (_attachedTo == null)
throw new InvalidOperationException("The control isn't currently attached to a toplevel");
- bounds *= _attachedTo.Window.Scaling;
+ bounds *= _attachedTo.Window.RenderScaling;
var pixelRect = new PixelRect((int)bounds.X, (int)bounds.Y, Math.Max(1, (int)bounds.Width),
Math.Max(1, (int)bounds.Height));
diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs
index 2a13999e8d..c24abcd230 100644
--- a/src/Avalonia.X11/X11Window.cs
+++ b/src/Avalonia.X11/X11Window.cs
@@ -163,7 +163,7 @@ namespace Avalonia.X11
var surfaces = new List
{
new X11FramebufferSurface(_x11.DeferredDisplay, _renderHandle,
- depth, () => Scaling)
+ depth, () => RenderScaling)
};
if (egl != null)
@@ -217,7 +217,7 @@ namespace Avalonia.X11
}
}
- public double Scaling => _window.Scaling;
+ public double Scaling => _window.RenderScaling;
}
void UpdateMotifHints()
@@ -284,9 +284,9 @@ namespace Avalonia.X11
XSetWMNormalHints(_x11.Display, _handle, ref hints);
}
- public Size ClientSize => new Size(_realSize.Width / Scaling, _realSize.Height / Scaling);
+ public Size ClientSize => new Size(_realSize.Width / RenderScaling, _realSize.Height / RenderScaling);
- public double Scaling
+ public double RenderScaling
{
get
{
@@ -296,6 +296,8 @@ namespace Avalonia.X11
}
private set => _scaling = value;
}
+
+ public double DesktopScaling => RenderScaling;
public IEnumerable Surfaces { get; }
public Action Input { get; set; }
@@ -538,14 +540,14 @@ namespace Avalonia.X11
{
var monitor = _platform.X11Screens.Screens.OrderBy(x => x.PixelDensity)
.FirstOrDefault(m => m.Bounds.Contains(Position));
- newScaling = monitor?.PixelDensity ?? Scaling;
+ newScaling = monitor?.PixelDensity ?? RenderScaling;
}
- if (Scaling != newScaling)
+ if (RenderScaling != newScaling)
{
var oldScaledSize = ClientSize;
- Scaling = newScaling;
- ScalingChanged?.Invoke(Scaling);
+ RenderScaling = newScaling;
+ ScalingChanged?.Invoke(RenderScaling);
SetMinMaxSize(_scaledMinMaxSize.minSize, _scaledMinMaxSize.maxSize);
if(!skipResize)
Resize(oldScaledSize, true);
@@ -707,9 +709,9 @@ namespace Avalonia.X11
private void ScheduleInput(RawInputEventArgs args)
{
if (args is RawPointerEventArgs mouse)
- mouse.Position = mouse.Position / Scaling;
+ mouse.Position = mouse.Position / RenderScaling;
if (args is RawDragEvent drag)
- drag.Location = drag.Location / Scaling;
+ drag.Location = drag.Location / RenderScaling;
_lastEvent = new InputEventContainer() {Event = args};
_inputQueue.Enqueue(_lastEvent);
@@ -760,11 +762,7 @@ namespace Avalonia.X11
public void Dispose()
{
- if (_handle != IntPtr.Zero)
- {
- XDestroyWindow(_x11.Display, _handle);
- Cleanup();
- }
+ Cleanup();
}
void Cleanup()
@@ -787,8 +785,7 @@ namespace Avalonia.X11
}
if (_useRenderWindow && _renderHandle != IntPtr.Zero)
- {
- XDestroyWindow(_x11.Display, _renderHandle);
+ {
_renderHandle = IntPtr.Zero;
}
}
@@ -821,11 +818,11 @@ namespace Avalonia.X11
public void Hide() => XUnmapWindow(_x11.Display, _handle);
- public Point PointToClient(PixelPoint point) => new Point((point.X - Position.X) / Scaling, (point.Y - Position.Y) / Scaling);
+ public Point PointToClient(PixelPoint point) => new Point((point.X - Position.X) / RenderScaling, (point.Y - Position.Y) / RenderScaling);
public PixelPoint PointToScreen(Point point) => new PixelPoint(
- (int)(point.X * Scaling + Position.X),
- (int)(point.Y * Scaling + Position.Y));
+ (int)(point.X * RenderScaling + Position.X),
+ (int)(point.Y * RenderScaling + Position.Y));
public void SetSystemDecorations(SystemDecorations enabled)
{
@@ -845,7 +842,7 @@ namespace Avalonia.X11
Resize(size, true);
}
- PixelSize ToPixelSize(Size size) => new PixelSize((int)(size.Width * Scaling), (int)(size.Height * Scaling));
+ PixelSize ToPixelSize(Size size) => new PixelSize((int)(size.Width * RenderScaling), (int)(size.Height * RenderScaling));
void Resize(Size clientSize, bool force)
{
@@ -1025,13 +1022,13 @@ namespace Avalonia.X11
{
_scaledMinMaxSize = (minSize, maxSize);
var min = new PixelSize(
- (int)(minSize.Width < 1 ? 1 : minSize.Width * Scaling),
- (int)(minSize.Height < 1 ? 1 : minSize.Height * Scaling));
+ (int)(minSize.Width < 1 ? 1 : minSize.Width * RenderScaling),
+ (int)(minSize.Height < 1 ? 1 : minSize.Height * RenderScaling));
const int maxDim = MaxWindowDimension;
var max = new PixelSize(
- (int)(maxSize.Width > maxDim ? maxDim : Math.Max(min.Width, maxSize.Width * Scaling)),
- (int)(maxSize.Height > maxDim ? maxDim : Math.Max(min.Height, maxSize.Height * Scaling)));
+ (int)(maxSize.Width > maxDim ? maxDim : Math.Max(min.Width, maxSize.Width * RenderScaling)),
+ (int)(maxSize.Height > maxDim ? maxDim : Math.Max(min.Height, maxSize.Height * RenderScaling)));
_minMaxSize = (min, max);
UpdateSizeHints(null);
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
index b8ae2eb4d8..0a101eec7a 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
@@ -65,7 +65,7 @@ namespace Avalonia.LinuxFramebuffer
public IMouseDevice MouseDevice => new MouseDevice();
public IPopupImpl CreatePopup() => null;
- public double Scaling => _outputBackend.Scaling;
+ public double RenderScaling => _outputBackend.Scaling;
public IEnumerable Surfaces { get; }
public Action Input { get; set; }
public Action Paint { get; set; }
@@ -77,7 +77,7 @@ namespace Avalonia.LinuxFramebuffer
public Action Closed { get; set; }
public Action LostFocus { get; set; }
- public Size ScaledSize => _outputBackend.PixelSize.ToSize(Scaling);
+ public Size ScaledSize => _outputBackend.PixelSize.ToSize(RenderScaling);
public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) { }
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
new file mode 100644
index 0000000000..768545eb7e
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
@@ -0,0 +1,14 @@
+
+
+
+ netstandard2.0
+ true
+
+
+
+
+
+
+
+
+
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs
new file mode 100644
index 0000000000..4569970d01
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs
@@ -0,0 +1,42 @@
+using System;
+using System.IO;
+using System.Reflection;
+using System.Text;
+using Avalonia.Markup.Xaml.XamlIl;
+// ReSharper disable CheckNamespace
+
+namespace Avalonia.Markup.Xaml
+{
+ public static class AvaloniaRuntimeXamlLoader
+ {
+ ///
+ /// Loads XAML from a string.
+ ///
+ /// The string containing the XAML.
+ /// Default assembly for clr-namespace:
+ ///
+ /// The optional instance into which the XAML should be loaded.
+ ///
+ /// The loaded object.
+ public static object Load(string xaml, Assembly localAssembly = null, object rootInstance = null, Uri uri = null, bool designMode = false)
+ {
+ Contract.Requires(xaml != null);
+
+ using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml)))
+ {
+ return Load(stream, localAssembly, rootInstance, uri, designMode);
+ }
+ }
+
+ public static object Load(Stream stream, Assembly localAssembly, object rootInstance = null, Uri uri = null,
+ bool designMode = false)
+ => AvaloniaXamlIlRuntimeCompiler.Load(stream, localAssembly, rootInstance, uri, designMode);
+
+ public static object Parse(string xaml, Assembly localAssembly = null)
+ => Load(xaml, localAssembly);
+
+ public static T Parse(string xaml, Assembly localAssembly = null)
+ => (T)Parse(xaml, localAssembly);
+
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AddNameScopeRegistration.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AddNameScopeRegistration.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaBindingExtensionTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaBindingExtensionTransformer.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaBindingExtensionTransformer.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaBindingExtensionTransformer.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlCompiledBindingsMetadataRemover.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlCompiledBindingsMetadataRemover.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlCompiledBindingsMetadataRemover.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlCompiledBindingsMetadataRemover.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs
similarity index 93%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs
index 5a0d6bac8d..241976241f 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs
@@ -1,11 +1,6 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
-using System.Text;
-using Avalonia.Markup.Parsers;
-using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
-using Avalonia.Utilities;
using XamlX;
using XamlX.Ast;
using XamlX.Transform;
@@ -129,12 +124,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
if (itemsCollectionType != null)
{
- var elementType = itemsCollectionType
- .GetAllInterfaces()
- .FirstOrDefault(i =>
- i.GenericTypeDefinition?.Equals(context.Configuration.WellKnownTypes.IEnumerableT) == true)
- .GenericArguments[0];
- return new AvaloniaXamlIlDataContextTypeMetadataNode(on, elementType);
+ foreach (var i in GetAllInterfacesIncludingSelf(itemsCollectionType))
+ {
+ if (i.GenericTypeDefinition?.Equals(context.Configuration.WellKnownTypes.IEnumerableT) == true)
+ {
+ return new AvaloniaXamlIlDataContextTypeMetadataNode(on, i.GenericArguments[0]);
+ }
+ }
}
// We can't infer the collection type and the currently calculated type is definitely wrong.
// Notify the user that we were unable to infer the data context type if they use a compiled binding.
@@ -165,6 +161,15 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
return new AvaloniaXamlIlUninferrableDataContextMetadataNode(on);
}
+
+ private static IEnumerable GetAllInterfacesIncludingSelf(IXamlType type)
+ {
+ if (type.IsInterface)
+ yield return type;
+
+ foreach (var i in type.GetAllInterfaces())
+ yield return i;
+ }
}
[DebuggerDisplay("DataType = {DataContextType}")]
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlDesignPropertiesTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDesignPropertiesTransformer.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlDesignPropertiesTransformer.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDesignPropertiesTransformer.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlMetadataRemover.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlMetadataRemover.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlMetadataRemover.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlMetadataRemover.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlPropertyPathTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlPropertyPathTransformer.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlPropertyPathTransformer.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlPropertyPathTransformer.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlResolveByNameMarkupExtensionReplacer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlResolveByNameMarkupExtensionReplacer.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlResolveByNameMarkupExtensionReplacer.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlResolveByNameMarkupExtensionReplacer.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlRootObjectScopeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlRootObjectScopeTransformer.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlRootObjectScopeTransformer.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlRootObjectScopeTransformer.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransitionsTypeMetadataTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransitionsTypeMetadataTransformer.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransitionsTypeMetadataTransformer.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransitionsTypeMetadataTransformer.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/IgnoredDirectivesTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/IgnoredDirectivesTransformer.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/IgnoredDirectivesTransformer.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/IgnoredDirectivesTransformer.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/XNameTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/XNameTransformer.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/XNameTransformer.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/XNameTransformer.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlPropertyInfoAccessorFactoryEmitter.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlPropertyInfoAccessorFactoryEmitter.cs
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlPropertyInfoAccessorFactoryEmitter.cs
rename to src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlPropertyInfoAccessorFactoryEmitter.cs
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/IncludeXamlIlSre.props b/src/Markup/Avalonia.Markup.Xaml.Loader/IncludeXamlIlSre.props
new file mode 100644
index 0000000000..c902fa956a
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/IncludeXamlIlSre.props
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
similarity index 100%
rename from src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
rename to src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
index 3979312ce0..24428253aa 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
@@ -45,44 +45,10 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -96,8 +62,6 @@
-
-
diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
index 5c21037924..e5c6b72d12 100644
--- a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
@@ -10,10 +10,13 @@ namespace Avalonia.Markup.Xaml
///
/// Loads XAML for a avalonia application.
///
- public class AvaloniaXamlLoader
+ public static class AvaloniaXamlLoader
{
- public bool IsDesignMode { get; set; }
-
+ public interface IRuntimeXamlLoader
+ {
+ object Load(Stream stream, Assembly localAsm, object o, Uri baseUri, bool designMode);
+ }
+
///
/// Loads the XAML into a Avalonia component.
///
@@ -32,7 +35,7 @@ namespace Avalonia.Markup.Xaml
/// A base URI to use if is relative.
///
/// The loaded object.
- public object Load(Uri uri, Uri baseUri = null)
+ public static object Load(Uri uri, Uri baseUri = null)
{
Contract.Requires(uri != null);
@@ -55,52 +58,22 @@ namespace Avalonia.Markup.Xaml
if (compiledResult != null)
return compiledResult;
}
-
-
- var asset = assetLocator.OpenAndGetAssembly(uri, baseUri);
- using (var stream = asset.stream)
- {
- var absoluteUri = uri.IsAbsoluteUri ? uri : new Uri(baseUri, uri);
- return Load(stream, asset.assembly, null, absoluteUri);
- }
- }
-
- ///
- /// Loads XAML from a string.
- ///
- /// The string containing the XAML.
- /// Default assembly for clr-namespace:
- ///
- /// The optional instance into which the XAML should be loaded.
- ///
- /// The loaded object.
- public object Load(string xaml, Assembly localAssembly = null, object rootInstance = null)
- {
- Contract.Requires(xaml != null);
- using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml)))
+ // This is intended for unit-tests only
+ var runtimeLoader = AvaloniaLocator.Current.GetService();
+ if (runtimeLoader != null)
{
- return Load(stream, localAssembly, rootInstance);
+ var asset = assetLocator.OpenAndGetAssembly(uri, baseUri);
+ using (var stream = asset.stream)
+ {
+ var absoluteUri = uri.IsAbsoluteUri ? uri : new Uri(baseUri, uri);
+ return runtimeLoader.Load(stream, asset.assembly, null, absoluteUri, false);
+ }
}
- }
- ///
- /// Loads XAML from a stream.
- ///
- /// The stream containing the XAML.
- /// Default assembly for clr-namespace
- ///
- /// The optional instance into which the XAML should be loaded.
- ///
- /// The URI of the XAML
- /// The loaded object.
- public object Load(Stream stream, Assembly localAssembly, object rootInstance = null, Uri uri = null)
- => AvaloniaXamlIlRuntimeCompiler.Load(stream, localAssembly, rootInstance, uri, IsDesignMode);
-
- public static object Parse(string xaml, Assembly localAssembly = null)
- => new AvaloniaXamlLoader().Load(xaml, localAssembly);
-
- public static T Parse(string xaml, Assembly localAssembly = null)
- => (T)Parse(xaml, localAssembly);
+ throw new XamlLoadException(
+ $"No precompiled XAML found for {uri} (baseUri: {baseUri}), make sure to specify x:Class and include your XAML file as AvaloniaResource");
+ }
+
}
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs
index 0cedf4f364..b6137aa89f 100644
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs
@@ -25,8 +25,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
if (_loaded == null)
{
_isLoading = true;
- var loader = new AvaloniaXamlLoader();
- _loaded = (IResourceDictionary)loader.Load(Source, _baseUri);
+ _loaded = (IResourceDictionary)AvaloniaXamlLoader.Load(Source, _baseUri);
_isLoading = false;
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
index ea9042f779..607b552c28 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
@@ -51,8 +51,7 @@ namespace Avalonia.Markup.Xaml.Styling
if (_loaded == null)
{
_isLoading = true;
- var loader = new AvaloniaXamlLoader();
- var loaded = (IStyle)loader.Load(Source, _baseUri);
+ var loaded = (IStyle)AvaloniaXamlLoader.Load(Source, _baseUri);
_loaded = new[] { loaded };
_isLoading = false;
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
index 5663d08412..07c5451135 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
@@ -5,7 +5,7 @@ using Avalonia.Metadata;
namespace Avalonia.Markup.Xaml.Templates
{
- public class DataTemplate : IDataTemplate
+ public class DataTemplate : IRecyclingDataTemplate
{
public Type DataType { get; set; }
@@ -14,8 +14,6 @@ namespace Avalonia.Markup.Xaml.Templates
[TemplateContent]
public object Content { get; set; }
- public bool SupportsRecycling { get; set; } = true;
-
public bool Match(object data)
{
if (DataType == null)
@@ -28,6 +26,11 @@ namespace Avalonia.Markup.Xaml.Templates
}
}
- public IControl Build(object data) => TemplateContent.Load(Content).Control;
+ public IControl Build(object data) => Build(data, null);
+
+ public IControl Build(object data, IControl existing)
+ {
+ return existing ?? TemplateContent.Load(Content).Control;
+ }
}
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
index b96486235a..b8e1c2df80 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
@@ -18,8 +18,6 @@ namespace Avalonia.Markup.Xaml.Templates
[AssignBinding]
public Binding ItemsSource { get; set; }
- public bool SupportsRecycling { get; set; } = true;
-
public bool Match(object data)
{
if (DataType == null)
diff --git a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs
index d1f8d6a779..5e630e54a6 100644
--- a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs
+++ b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs
@@ -570,7 +570,7 @@ namespace Avalonia.Skia
float constraint = -1;
- if (_wrapping != TextWrapping.NoWrap)
+ if (_wrapping == TextWrapping.Wrap)
{
constraint = widthConstraint <= 0 ? MAX_LINE_WIDTH : widthConstraint;
if (constraint > MAX_LINE_WIDTH)
diff --git a/src/Skia/Avalonia.Skia/TextShaperImpl.cs b/src/Skia/Avalonia.Skia/TextShaperImpl.cs
index ffe1175567..b0384a1fdf 100644
--- a/src/Skia/Avalonia.Skia/TextShaperImpl.cs
+++ b/src/Skia/Avalonia.Skia/TextShaperImpl.cs
@@ -82,7 +82,7 @@ namespace Avalonia.Skia
if (codepoint.IsBreakChar)
{
- if (i < text.End)
+ if (i + 1 < text.Length)
{
var nextCodepoint = Codepoint.ReadAt(text, i + 1, out _);
diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
index f5d83611bb..3467a33d16 100644
--- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
+++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
@@ -75,7 +75,7 @@ namespace Avalonia.Win32.Interop.Wpf
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
{
if (msg == (int)UnmanagedMethods.WindowsMessage.WM_DPICHANGED)
- _ttl.ScalingChanged?.Invoke(_ttl.Scaling);
+ _ttl.ScalingChanged?.Invoke(_ttl.RenderScaling);
return IntPtr.Zero;
}
@@ -84,7 +84,7 @@ namespace Avalonia.Win32.Interop.Wpf
_currentHwndSource?.RemoveHook(_hook);
_currentHwndSource = e.NewSource as HwndSource;
_currentHwndSource?.AddHook(_hook);
- _ttl.ScalingChanged?.Invoke(_ttl.Scaling);
+ _ttl.ScalingChanged?.Invoke(_ttl.RenderScaling);
}
public IRenderer CreateRenderer(IRenderRoot root)
@@ -102,7 +102,7 @@ namespace Avalonia.Win32.Interop.Wpf
Size ITopLevelImpl.ClientSize => _finalSize;
IMouseDevice ITopLevelImpl.MouseDevice => _mouse;
- double ITopLevelImpl.Scaling => PresentationSource.FromVisual(this)?.CompositionTarget?.TransformToDevice.M11 ?? 1;
+ double ITopLevelImpl.RenderScaling => PresentationSource.FromVisual(this)?.CompositionTarget?.TransformToDevice.M11 ?? 1;
IEnumerable ITopLevelImpl.Surfaces => _surfaces;
diff --git a/src/Windows/Avalonia.Win32/PopupImpl.cs b/src/Windows/Avalonia.Win32/PopupImpl.cs
index 525e5e0d52..57da1c4d66 100644
--- a/src/Windows/Avalonia.Win32/PopupImpl.cs
+++ b/src/Windows/Avalonia.Win32/PopupImpl.cs
@@ -57,7 +57,7 @@ namespace Avalonia.Win32
{
var info = UnmanagedMethods.MONITORINFO.Create();
UnmanagedMethods.GetMonitorInfo(monitor, ref info);
- _maxAutoSize = info.rcWork.ToPixelRect().ToRect(Scaling).Size;
+ _maxAutoSize = info.rcWork.ToPixelRect().ToRect(RenderScaling).Size;
}
}
diff --git a/src/Windows/Avalonia.Win32/Win32NativeControlHost.cs b/src/Windows/Avalonia.Win32/Win32NativeControlHost.cs
index d7bb2c037e..8f62163d81 100644
--- a/src/Windows/Avalonia.Win32/Win32NativeControlHost.cs
+++ b/src/Windows/Avalonia.Win32/Win32NativeControlHost.cs
@@ -176,7 +176,7 @@ namespace Avalonia.Win32
UnmanagedMethods.SetWindowPosFlags.SWP_NOACTIVATE);
if (_attachedTo == null || _child == null)
return;
- size *= _attachedTo.Window.Scaling;
+ size *= _attachedTo.Window.RenderScaling;
UnmanagedMethods.MoveWindow(_child.Handle, 0, 0,
Math.Max(1, (int)size.Width), Math.Max(1, (int)size.Height), false);
}
@@ -186,7 +186,7 @@ namespace Avalonia.Win32
CheckDisposed();
if (_attachedTo == null)
throw new InvalidOperationException("The control isn't currently attached to a toplevel");
- bounds *= _attachedTo.Window.Scaling;
+ bounds *= _attachedTo.Window.RenderScaling;
var pixelRect = new PixelRect((int)bounds.X, (int)bounds.Y, Math.Max(1, (int)bounds.Width),
Math.Max(1, (int)bounds.Height));
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
index 0ba1d311bc..ee6845e2eb 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
@@ -343,7 +343,7 @@ namespace Avalonia.Win32
{
if (BeginPaint(_hwnd, out PAINTSTRUCT ps) != IntPtr.Zero)
{
- var f = Scaling;
+ var f = RenderScaling;
var r = ps.rcPaint;
Paint?.Invoke(new Rect(r.left / f, r.top / f, (r.right - r.left) / f,
(r.bottom - r.top) / f));
@@ -368,7 +368,7 @@ namespace Avalonia.Win32
size == SizeCommand.Maximized))
{
var clientSize = new Size(ToInt32(lParam) & 0xffff, ToInt32(lParam) >> 16);
- Resized(clientSize / Scaling);
+ Resized(clientSize / RenderScaling);
}
var windowState = size == SizeCommand.Maximized ?
@@ -406,25 +406,25 @@ namespace Avalonia.Win32
if (_minSize.Width > 0)
{
mmi.ptMinTrackSize.X =
- (int)((_minSize.Width * Scaling) + BorderThickness.Left + BorderThickness.Right);
+ (int)((_minSize.Width * RenderScaling) + BorderThickness.Left + BorderThickness.Right);
}
if (_minSize.Height > 0)
{
mmi.ptMinTrackSize.Y =
- (int)((_minSize.Height * Scaling) + BorderThickness.Top + BorderThickness.Bottom);
+ (int)((_minSize.Height * RenderScaling) + BorderThickness.Top + BorderThickness.Bottom);
}
if (!double.IsInfinity(_maxSize.Width) && _maxSize.Width > 0)
{
mmi.ptMaxTrackSize.X =
- (int)((_maxSize.Width * Scaling) + BorderThickness.Left + BorderThickness.Right);
+ (int)((_maxSize.Width * RenderScaling) + BorderThickness.Left + BorderThickness.Right);
}
if (!double.IsInfinity(_maxSize.Height) && _maxSize.Height > 0)
{
mmi.ptMaxTrackSize.Y =
- (int)((_maxSize.Height * Scaling) + BorderThickness.Top + BorderThickness.Bottom);
+ (int)((_maxSize.Height * RenderScaling) + BorderThickness.Top + BorderThickness.Bottom);
}
Marshal.StructureToPtr(mmi, lParam, true);
@@ -480,7 +480,7 @@ namespace Avalonia.Win32
private Point DipFromLParam(IntPtr lParam)
{
- return new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)) / Scaling;
+ return new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)) / RenderScaling;
}
private PixelPoint PointFromLParam(IntPtr lParam)
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs
index 2badf99f7f..a3b7574369 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs
@@ -37,7 +37,7 @@ namespace Avalonia.Win32
if (_extendTitleBarHint >= 0)
{
- border_thickness.top = (int)(_extendedMargins.Top * Scaling);
+ border_thickness.top = (int)(_extendedMargins.Top * RenderScaling);
}
// Determine if the hit test is for resizing. Default middle (1,1).
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index 0ee1342d27..6f22f94056 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -164,7 +164,9 @@ namespace Avalonia.Win32
}
}
- public double Scaling => _scaling;
+ public double RenderScaling => _scaling;
+
+ public double DesktopScaling => RenderScaling;
public Size ClientSize
{
@@ -172,7 +174,7 @@ namespace Avalonia.Win32
{
GetClientRect(_hwnd, out var rect);
- return new Size(rect.right, rect.bottom) / Scaling;
+ return new Size(rect.right, rect.bottom) / RenderScaling;
}
}
@@ -180,7 +182,7 @@ namespace Avalonia.Win32
public IPlatformHandle Handle { get; private set; }
- public virtual Size MaxAutoSizeHint => new Size(_maxTrackSize.X / Scaling, _maxTrackSize.Y / Scaling);
+ public virtual Size MaxAutoSizeHint => new Size(_maxTrackSize.X / RenderScaling, _maxTrackSize.Y / RenderScaling);
public IMouseDevice MouseDevice => _mouseDevice;
@@ -342,8 +344,8 @@ namespace Avalonia.Win32
public void Resize(Size value)
{
- int requestedClientWidth = (int)(value.Width * Scaling);
- int requestedClientHeight = (int)(value.Height * Scaling);
+ int requestedClientWidth = (int)(value.Width * RenderScaling);
+ int requestedClientHeight = (int)(value.Height * RenderScaling);
GetClientRect(_hwnd, out var clientRect);
@@ -395,7 +397,7 @@ namespace Avalonia.Win32
public void Invalidate(Rect rect)
{
- var scaling = Scaling;
+ var scaling = RenderScaling;
var r = new RECT
{
left = (int)Math.Floor(rect.X * scaling),
@@ -411,12 +413,12 @@ namespace Avalonia.Win32
{
var p = new POINT { X = point.X, Y = point.Y };
UnmanagedMethods.ScreenToClient(_hwnd, ref p);
- return new Point(p.X, p.Y) / Scaling;
+ return new Point(p.X, p.Y) / RenderScaling;
}
public PixelPoint PointToScreen(Point point)
{
- point *= Scaling;
+ point *= RenderScaling;
var p = new POINT { X = (int)point.X, Y = (int)point.Y };
ClientToScreen(_hwnd, ref p);
return new PixelPoint(p.X, p.Y);
@@ -710,19 +712,19 @@ namespace Avalonia.Win32
if (_extendTitleBarHint != -1)
{
- borderCaptionThickness.top = (int)(_extendTitleBarHint * Scaling);
+ borderCaptionThickness.top = (int)(_extendTitleBarHint * RenderScaling);
}
margins.cyTopHeight = _extendChromeHints.HasFlag(ExtendClientAreaChromeHints.SystemChrome) && !_extendChromeHints.HasFlag(ExtendClientAreaChromeHints.PreferSystemChrome) ? borderCaptionThickness.top : 1;
if (WindowState == WindowState.Maximized)
{
- _extendedMargins = new Thickness(0, (borderCaptionThickness.top - borderThickness.top) / Scaling, 0, 0);
- _offScreenMargin = new Thickness(borderThickness.left / Scaling, borderThickness.top / Scaling, borderThickness.right / Scaling, borderThickness.bottom / Scaling);
+ _extendedMargins = new Thickness(0, (borderCaptionThickness.top - borderThickness.top) / RenderScaling, 0, 0);
+ _offScreenMargin = new Thickness(borderThickness.left / RenderScaling, borderThickness.top / RenderScaling, borderThickness.right / RenderScaling, borderThickness.bottom / RenderScaling);
}
else
{
- _extendedMargins = new Thickness(0, (borderCaptionThickness.top) / Scaling, 0, 0);
+ _extendedMargins = new Thickness(0, (borderCaptionThickness.top) / RenderScaling, 0, 0);
_offScreenMargin = new Thickness();
}
@@ -1034,6 +1036,8 @@ namespace Avalonia.Win32
}
}
+ double EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Scaling => RenderScaling;
+
IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => Handle.Handle;
public void SetExtendClientAreaToDecorationsHint(bool hint)
diff --git a/src/iOS/Avalonia.iOS/TopLevelImpl.cs b/src/iOS/Avalonia.iOS/TopLevelImpl.cs
index 83a68990d7..5a85a5ea88 100644
--- a/src/iOS/Avalonia.iOS/TopLevelImpl.cs
+++ b/src/iOS/Avalonia.iOS/TopLevelImpl.cs
@@ -48,7 +48,7 @@ namespace Avalonia.iOS
public new IPlatformHandle Handle => null;
- public double Scaling => UIScreen.MainScreen.Scale;
+ public double RenderScaling => UIScreen.MainScreen.Scale;
public override void LayoutSubviews() => Resized?.Invoke(ClientSize);
diff --git a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
index 1c7077870a..51d18e55d1 100644
--- a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
+++ b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
@@ -18,6 +18,10 @@
-
+
+
+
+
+
diff --git a/src/tools/Avalonia.Designer.HostApp/DesignXamlLoader.cs b/src/tools/Avalonia.Designer.HostApp/DesignXamlLoader.cs
new file mode 100644
index 0000000000..7af29a56a1
--- /dev/null
+++ b/src/tools/Avalonia.Designer.HostApp/DesignXamlLoader.cs
@@ -0,0 +1,16 @@
+using System;
+using System.IO;
+using System.Reflection;
+using Avalonia.Markup.Xaml;
+using Avalonia.Markup.Xaml.XamlIl;
+
+namespace Avalonia.Designer.HostApp
+{
+ class DesignXamlLoader : AvaloniaXamlLoader.IRuntimeXamlLoader
+ {
+ public object Load(Stream stream, Assembly localAsm, object o, Uri baseUri, bool designMode)
+ {
+ return AvaloniaXamlIlRuntimeCompiler.Load(stream, localAsm, o, baseUri, designMode);
+ }
+ }
+}
diff --git a/src/tools/Avalonia.Designer.HostApp/Program.cs b/src/tools/Avalonia.Designer.HostApp/Program.cs
index 3163e1fbc3..4472dac4e3 100644
--- a/src/tools/Avalonia.Designer.HostApp/Program.cs
+++ b/src/tools/Avalonia.Designer.HostApp/Program.cs
@@ -1,6 +1,8 @@
using System;
using System.IO;
using System.Reflection;
+using Avalonia.DesignerSupport;
+using Avalonia.Markup.Xaml;
namespace Avalonia.Designer.HostApp
{
@@ -40,8 +42,9 @@ namespace Avalonia.Designer.HostApp
public static void Main(string[] args)
#endif
{
+ AvaloniaLocator.CurrentMutable.Bind()
+ .ToConstant(new DesignXamlLoader());
Avalonia.DesignerSupport.Remote.RemoteDesignerEntryPoint.Main(args);
}
-
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj
index 2ca93dcf56..19c4454d3d 100644
--- a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj
+++ b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj
@@ -13,6 +13,7 @@
+
diff --git a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
index 9a81d19bb9..7a2109e5a7 100644
--- a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
@@ -191,8 +191,7 @@ namespace Avalonia.Controls.UnitTests
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var target1 = window.Find("target1");
var target2 = window.Find("target2");
var mouse = new MouseTestHelper();
@@ -235,8 +234,7 @@ namespace Avalonia.Controls.UnitTests
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var target1 = window.Find("target1");
var target2 = window.Find("target2");
var mouse = new MouseTestHelper();
@@ -298,7 +296,7 @@ namespace Avalonia.Controls.UnitTests
var windowImpl = MockWindowingPlatform.CreateWindowMock();
popupImpl = MockWindowingPlatform.CreatePopupMock(windowImpl.Object);
- popupImpl.SetupGet(x => x.Scaling).Returns(1);
+ popupImpl.SetupGet(x => x.RenderScaling).Returns(1);
windowImpl.Setup(x => x.CreatePopup()).Returns(popupImpl.Object);
windowImpl.Setup(x => x.Screen).Returns(screenImpl.Object);
diff --git a/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs b/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs
index dee7a84812..84f02aeda5 100644
--- a/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs
@@ -191,7 +191,8 @@ namespace Avalonia.Controls.UnitTests
{
var windowImpl = new Mock();
windowImpl.SetupProperty(x => x.Closed);
- windowImpl.Setup(x => x.Scaling).Returns(1);
+ windowImpl.Setup(x => x.DesktopScaling).Returns(1);
+ windowImpl.Setup(x => x.RenderScaling).Returns(1);
var services = TestServices.StyledWindow.With(
windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object));
diff --git a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs
index db9211ac3c..fd52aeb9af 100644
--- a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs
@@ -8,6 +8,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Controls.Utils;
using Avalonia.LogicalTree;
+using Avalonia.Markup.Xaml;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Xunit;
@@ -338,8 +339,7 @@ namespace Avalonia.Controls.UnitTests
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
";
- var loader = new Markup.Xaml.AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var tabControl = window.FindControl("tabs");
tabControl.DataContext = new { Tabs = new List() };
diff --git a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
index e49e273bec..6b30aed257 100644
--- a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
@@ -93,7 +93,7 @@ namespace Avalonia.Controls.UnitTests
{
var impl = new Mock();
impl.SetupProperty(x => x.Resized);
- impl.SetupGet(x => x.Scaling).Returns(1);
+ impl.SetupGet(x => x.RenderScaling).Returns(1);
var target = new TestTopLevel(impl.Object)
{
@@ -290,7 +290,7 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock();
- impl.SetupGet(x => x.Scaling).Returns(1);
+ impl.SetupGet(x => x.RenderScaling).Returns(1);
var child = new Border { Classes = { "foo" } };
var target = new TestTopLevel(impl.Object)
diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
index c1bd45bcad..c25ad19027 100644
--- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
@@ -1273,8 +1273,6 @@ namespace Avalonia.Controls.UnitTests
return new TextBlock { Text = node.Value };
}
- public bool SupportsRecycling => false;
-
public InstancedBinding ItemsSelector(object item)
{
var obs = ExpressionObserver.Create(item, o => (o as Node).Children);
diff --git a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
index 697ea9cff8..84f212d1b3 100644
--- a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
@@ -110,7 +110,8 @@ namespace Avalonia.Controls.UnitTests
public void IsVisible_Should_Be_False_Atfer_Impl_Signals_Close()
{
var windowImpl = new Mock();
- windowImpl.Setup(x => x.Scaling).Returns(1);
+ windowImpl.Setup(x => x.DesktopScaling).Returns(1);
+ windowImpl.Setup(x => x.RenderScaling).Returns(1);
windowImpl.SetupProperty(x => x.Closed);
using (UnitTestApplication.Start(TestServices.StyledWindow))
@@ -128,7 +129,8 @@ namespace Avalonia.Controls.UnitTests
public void Setting_IsVisible_True_Shows_Window()
{
var windowImpl = new Mock();
- windowImpl.Setup(x => x.Scaling).Returns(1);
+ windowImpl.Setup(x => x.DesktopScaling).Returns(1);
+ windowImpl.Setup(x => x.RenderScaling).Returns(1);
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
@@ -143,7 +145,8 @@ namespace Avalonia.Controls.UnitTests
public void Setting_IsVisible_False_Hides_Window()
{
var windowImpl = new Mock();
- windowImpl.Setup(x => x.Scaling).Returns(1);
+ windowImpl.Setup(x => x.DesktopScaling).Returns(1);
+ windowImpl.Setup(x => x.RenderScaling).Returns(1);
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
@@ -208,7 +211,8 @@ namespace Avalonia.Controls.UnitTests
{
var renderer = new Mock();
var windowImpl = new Mock();
- windowImpl.Setup(x => x.Scaling).Returns(1);
+ windowImpl.Setup(x => x.DesktopScaling).Returns(1);
+ windowImpl.Setup(x => x.RenderScaling).Returns(1);
windowImpl.SetupProperty(x => x.Closed);
windowImpl.Setup(x => x.CreateRenderer(It.IsAny())).Returns(renderer.Object);
@@ -237,7 +241,7 @@ namespace Avalonia.Controls.UnitTests
public TestWindowBase(IRenderer renderer = null)
: base(Mock.Of(x =>
- x.Scaling == 1 &&
+ x.RenderScaling == 1 &&
x.CreateRenderer(It.IsAny()) == renderer))
{
}
diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs
index e2b0def00b..ba29001cf3 100644
--- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs
@@ -100,7 +100,8 @@ namespace Avalonia.Controls.UnitTests
{
var windowImpl = new Mock();
windowImpl.SetupProperty(x => x.Closed);
- windowImpl.Setup(x => x.Scaling).Returns(1);
+ windowImpl.Setup(x => x.DesktopScaling).Returns(1);
+ windowImpl.Setup(x => x.RenderScaling).Returns(1);
var services = TestServices.StyledWindow.With(
windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object));
@@ -206,7 +207,8 @@ namespace Avalonia.Controls.UnitTests
var parent = new Mock();
var windowImpl = new Mock();
windowImpl.SetupProperty(x => x.Closed);
- windowImpl.Setup(x => x.Scaling).Returns(1);
+ windowImpl.Setup(x => x.DesktopScaling).Returns(1);
+ windowImpl.Setup(x => x.RenderScaling).Returns(1);
var target = new Window(windowImpl.Object);
var task = target.ShowDialog(parent.Object);
@@ -245,7 +247,8 @@ namespace Avalonia.Controls.UnitTests
var parent = new Mock();
var windowImpl = new Mock();
windowImpl.SetupProperty(x => x.Closed);
- windowImpl.Setup(x => x.Scaling).Returns(1);
+ windowImpl.Setup(x => x.DesktopScaling).Returns(1);
+ windowImpl.Setup(x => x.RenderScaling).Returns(1);
var target = new Window(windowImpl.Object);
var task = target.ShowDialog(parent.Object);
@@ -273,7 +276,8 @@ namespace Avalonia.Controls.UnitTests
var windowImpl = MockWindowingPlatform.CreateWindowMock();
windowImpl.Setup(x => x.ClientSize).Returns(new Size(800, 480));
- windowImpl.Setup(x => x.Scaling).Returns(1);
+ windowImpl.Setup(x => x.DesktopScaling).Returns(1);
+ windowImpl.Setup(x => x.RenderScaling).Returns(1);
windowImpl.Setup(x => x.Screen).Returns(screens.Object);
using (UnitTestApplication.Start(TestServices.StyledWindow))
@@ -298,12 +302,14 @@ namespace Avalonia.Controls.UnitTests
var parentWindowImpl = MockWindowingPlatform.CreateWindowMock();
parentWindowImpl.Setup(x => x.ClientSize).Returns(new Size(800, 480));
parentWindowImpl.Setup(x => x.MaxAutoSizeHint).Returns(new Size(1920, 1080));
- parentWindowImpl.Setup(x => x.Scaling).Returns(1);
+ parentWindowImpl.Setup(x => x.DesktopScaling).Returns(1);
+ parentWindowImpl.Setup(x => x.RenderScaling).Returns(1);
var windowImpl = MockWindowingPlatform.CreateWindowMock();
windowImpl.Setup(x => x.ClientSize).Returns(new Size(320, 200));
windowImpl.Setup(x => x.MaxAutoSizeHint).Returns(new Size(1920, 1080));
- windowImpl.Setup(x => x.Scaling).Returns(1);
+ windowImpl.Setup(x => x.DesktopScaling).Returns(1);
+ windowImpl.Setup(x => x.RenderScaling).Returns(1);
var parentWindowServices = TestServices.StyledWindow.With(
windowingPlatform: new MockWindowingPlatform(() => parentWindowImpl.Object));
@@ -565,7 +571,7 @@ namespace Avalonia.Controls.UnitTests
private IWindowImpl CreateImpl(Mock renderer)
{
return Mock.Of(x =>
- x.Scaling == 1 &&
+ x.RenderScaling == 1 &&
x.CreateRenderer(It.IsAny()) == renderer.Object);
}
diff --git a/tests/Avalonia.Controls.UnitTests/WindowingPlatformMock.cs b/tests/Avalonia.Controls.UnitTests/WindowingPlatformMock.cs
index 25e8c82b1a..bf1322afbc 100644
--- a/tests/Avalonia.Controls.UnitTests/WindowingPlatformMock.cs
+++ b/tests/Avalonia.Controls.UnitTests/WindowingPlatformMock.cs
@@ -17,7 +17,7 @@ namespace Avalonia.Controls.UnitTests
public IWindowImpl CreateWindow()
{
- return _windowImpl?.Invoke() ?? Mock.Of(x => x.Scaling == 1);
+ return _windowImpl?.Invoke() ?? Mock.Of(x => x.RenderScaling == 1);
}
public IWindowImpl CreateEmbeddableWindow()
@@ -25,6 +25,6 @@ namespace Avalonia.Controls.UnitTests
throw new NotImplementedException();
}
- public IPopupImpl CreatePopup() => _popupImpl?.Invoke() ?? Mock.Of(x => x.Scaling == 1);
+ public IPopupImpl CreatePopup() => _popupImpl?.Invoke() ?? Mock.Of(x => x.RenderScaling == 1);
}
}
diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs
index 00ef503b8d..530b8fa20c 100644
--- a/tests/Avalonia.LeakTests/ControlTests.cs
+++ b/tests/Avalonia.LeakTests/ControlTests.cs
@@ -355,7 +355,7 @@ namespace Avalonia.LeakTests
var renderer = new Mock();
renderer.Setup(x => x.Dispose());
var impl = new Mock();
- impl.SetupGet(x => x.Scaling).Returns(1);
+ impl.SetupGet(x => x.RenderScaling).Returns(1);
impl.SetupProperty(x => x.Closed);
impl.Setup(x => x.CreateRenderer(It.IsAny())).Returns(renderer.Object);
impl.Setup(x => x.Dispose()).Callback(() => impl.Object.Closed());
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
index e8c4daa7bc..ad3592294d 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
@@ -11,6 +11,7 @@
+
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/ConverterTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/ConverterTests.cs
index b424003ed6..c9420f1696 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/ConverterTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/ConverterTests.cs
@@ -9,7 +9,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Converters
public void Bug_2228_Relative_Uris_Should_Be_Correctly_Parsed()
{
var testClass = typeof(TestClassWithUri);
- var parsed = AvaloniaXamlLoader.Parse(
+ var parsed = AvaloniaRuntimeXamlLoader.Parse(
$"<{testClass.Name} xmlns='clr-namespace:{testClass.Namespace}' Uri='/test'/>", testClass.Assembly);
Assert.False(parsed.Uri.IsAbsoluteUri);
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/MultiValueConverterTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/MultiValueConverterTests.cs
index a77723afe1..466ae1bf7c 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/MultiValueConverterTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/MultiValueConverterTests.cs
@@ -29,8 +29,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Converters
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl("textBlock");
window.ApplyTemplate();
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/NullableConverterTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/NullableConverterTests.cs
index cdd40ed80f..eb8851c80b 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/NullableConverterTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/NullableConverterTests.cs
@@ -22,8 +22,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Converters
xmlns='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Converters'
Thickness = '5' Orientation='Vertical'
>";
- var loader = new AvaloniaXamlLoader();
- var data = (ClassWithNullableProperties)loader.Load(xaml, typeof(ClassWithNullableProperties).Assembly);
+ var data = (ClassWithNullableProperties)AvaloniaRuntimeXamlLoader.Load(xaml, typeof(ClassWithNullableProperties).Assembly);
Assert.Equal(new Thickness(5), data.Thickness);
Assert.Equal(Orientation.Vertical, data.Orientation);
}
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/PointsListTypeConverterTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/PointsListTypeConverterTests.cs
index b060905f38..3b729e9cd8 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/PointsListTypeConverterTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/PointsListTypeConverterTests.cs
@@ -31,8 +31,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Converters
public void Should_Parse_Points_in_Xaml(string input)
{
var xaml = $"";
- var loader = new AvaloniaXamlLoader();
- var polygon = (Polygon)loader.Load(xaml);
+ var polygon = (Polygon)AvaloniaRuntimeXamlLoader.Load(xaml);
var points = polygon.Points;
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/ValueConverterTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/ValueConverterTests.cs
index 5e698117c3..4d5983e276 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/ValueConverterTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/ValueConverterTests.cs
@@ -21,8 +21,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Converters
xmlns:c='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Converters;assembly=Avalonia.Markup.Xaml.UnitTests'>
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl("textBlock");
window.ApplyTemplate();
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs
index 6730e3134d..afc4a36fea 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs
@@ -24,8 +24,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl("textBlock");
window.DataContext = "foo";
@@ -45,8 +44,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl("textBlock");
window.ApplyTemplate();
@@ -86,8 +84,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBox = window.FindControl("textBox");
window.ApplyTemplate();
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Method.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Method.cs
index b2b4c4da1a..a7a004bd49 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Method.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Method.cs
@@ -20,8 +20,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = window.FindControl";
- var grid = AvaloniaXamlLoader.Parse(xaml);
+ var grid = AvaloniaRuntimeXamlLoader.Parse(xaml);
Assert.Equal(4, grid.ColumnDefinitions.Count);
Assert.Equal(4, grid.RowDefinitions.Count);
@@ -259,7 +259,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
RowDefinitions='100,Auto,*,100*'>
";
- var grid = AvaloniaXamlLoader.Parse(xaml);
+ var grid = AvaloniaRuntimeXamlLoader.Parse(xaml);
Assert.Equal(4, grid.ColumnDefinitions.Count);
@@ -291,7 +291,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var template = AvaloniaXamlLoader.Parse(xaml);
+ var template = AvaloniaRuntimeXamlLoader.Parse(xaml);
var parent = (ContentControl)template.Build(new ContentControl()).Control;
@@ -314,7 +314,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var template = AvaloniaXamlLoader.Parse(xaml);
+ var template = AvaloniaRuntimeXamlLoader.Parse(xaml);
Assert.Equal(typeof(ContentControl), template.TargetType);
@@ -332,7 +332,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var template = AvaloniaXamlLoader.Parse(xaml);
+ var template = AvaloniaRuntimeXamlLoader.Parse(xaml);
var panel = (Panel)template.Build(new ContentControl()).Control;
@@ -354,7 +354,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Foo
";
- var control = AvaloniaXamlLoader.Parse(xaml);
+ var control = AvaloniaRuntimeXamlLoader.Parse(xaml);
var button = control.FindControl("button");
Assert.Equal("Foo", button.Content);
@@ -365,7 +365,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
{
var xaml = @"";
- var control = AvaloniaXamlLoader.Parse(xaml);
+ var control = AvaloniaRuntimeXamlLoader.Parse(xaml);
Assert.Equal(200.5, control.Width);
}
@@ -374,7 +374,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
{
var xaml = @"";
- var control = AvaloniaXamlLoader.Parse(xaml);
+ var control = AvaloniaRuntimeXamlLoader.Parse(xaml);
var bk = control.Background;
Assert.IsType(bk);
Assert.Equal(Colors.White, (bk as ISolidColorBrush).Color);
@@ -392,7 +392,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var styles = AvaloniaXamlLoader.Parse(xaml);
+ var styles = AvaloniaRuntimeXamlLoader.Parse(xaml);
Assert.Single(styles);
@@ -420,7 +420,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var styles = AvaloniaXamlLoader.Parse(xaml);
+ var styles = AvaloniaRuntimeXamlLoader.Parse(xaml);
Assert.Single(styles);
@@ -474,7 +474,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var styles = AvaloniaXamlLoader.Parse(xaml);
+ var styles = AvaloniaRuntimeXamlLoader.Parse(xaml);
Assert.Single(styles);
@@ -505,7 +505,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var style = AvaloniaXamlLoader.Parse";
- var styles = AvaloniaXamlLoader.Parse(xaml);
+ var styles = AvaloniaRuntimeXamlLoader.Parse(xaml);
Assert.True(styles.Count == 1);
@@ -554,7 +554,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
var xaml =
@"";
- var target = AvaloniaXamlLoader.Parse(xaml);
+ var target = AvaloniaRuntimeXamlLoader.Parse(xaml);
Assert.Null(target.Content);
@@ -573,7 +573,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
var xaml =
@"";
- var target = AvaloniaXamlLoader.Parse(xaml);
+ var target = AvaloniaRuntimeXamlLoader.Parse(xaml);
Assert.Null(target.Content);
@@ -596,7 +596,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var target = AvaloniaXamlLoader.Parse(xaml);
+ var target = AvaloniaRuntimeXamlLoader.Parse(xaml);
Assert.NotNull(target.Content);
@@ -622,7 +622,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var target = AvaloniaXamlLoader.Parse(xaml);
+ var target = AvaloniaRuntimeXamlLoader.Parse(xaml);
Assert.Equal(2, target.Bindings.Count);
@@ -651,7 +651,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var target = AvaloniaXamlLoader.Parse(xaml);
+ var target = AvaloniaRuntimeXamlLoader.Parse(xaml);
Assert.NotNull(target.Template);
@@ -681,7 +681,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var style = AvaloniaXamlLoader.Parse
";
- var loader = new AvaloniaXamlLoader();
- var userControl = (UserControl)loader.Load(xaml);
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml);
var color = (Color)((Style)userControl.Styles[0]).Resources["color"];
Assert.Equal(0xff506070, color.ToUint32());
@@ -52,8 +51,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var loader = new AvaloniaXamlLoader();
- var userControl = (UserControl)loader.Load(xaml);
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml);
var dataTemplate = (DataTemplate)((Style)userControl.Styles[0]).Resources["dataTemplate"];
Assert.NotNull(dataTemplate);
@@ -78,8 +76,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var loader = new AvaloniaXamlLoader();
- var userControl = (UserControl)loader.Load(xaml);
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml);
var controlTemplate = (ControlTemplate)((Style)userControl.Styles[0]).Resources["controlTemplate"];
Assert.NotNull(controlTemplate);
@@ -103,8 +100,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var loader = new AvaloniaXamlLoader();
- var userControl = (UserControl)loader.Load(xaml);
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml);
var brush = (SolidColorBrush)((Style)userControl.Styles[0]).Resources["brush"];
Assert.Equal(0xff506070, brush.Color.ToUint32());
@@ -124,7 +120,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var window = AvaloniaXamlLoader.Parse(xaml);
+ var window = AvaloniaRuntimeXamlLoader.Parse(xaml);
Assert.Single(window.Styles);
@@ -157,8 +153,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var target = window.Find("target");
Assert.IsType(target.Content);
@@ -189,8 +184,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var target = window.Find("target");
Assert.NotNull(target.FocusAdorner);
@@ -213,8 +207,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = (TextBlock)window.Content;
window.ApplyTemplate();
@@ -239,8 +232,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var loader = new AvaloniaXamlLoader();
- var ex = Assert.Throws(() => loader.Load(xaml));
+ var ex = Assert.Throws(() => AvaloniaRuntimeXamlLoader.Load(xaml));
Assert.Equal(
"Property 'Button.IsDefault' is not registered on 'Avalonia.Controls.TextBlock'.",
@@ -266,8 +258,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var foo = window.FindControl("foo");
var notFoo = window.FindControl("notFoo");
@@ -295,8 +286,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var foo = window.FindControl("foo");
var bar = window.FindControl("bar");
var baz = window.FindControl("baz");
@@ -326,8 +316,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = window.FindControl("button");
var carousel = window.FindControl("carousel");
var listBox = window.FindControl("listBox");
@@ -364,8 +353,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var border = (Border)window.Content;
Assert.Equal(1, border.Transitions.Count);
@@ -400,8 +388,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var foo = window.FindControl("foo");
Assert.Equal(Colors.Red, ((ISolidColorBrush)foo.Background).Color);
@@ -425,8 +412,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var foo = window.FindControl("foo");
Assert.Null(foo.Background);
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs
index ed99156fad..3fdac49f31 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs
@@ -15,8 +15,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
using (UnitTestApplication.Start(TestServices.MockPlatformWrapper))
{
var xaml = "";
- var loader = new AvaloniaXamlLoader();
- var templates = (DataTemplates)loader.Load(xaml);
+ var templates = (DataTemplates)AvaloniaRuntimeXamlLoader.Load(xaml);
var template = (TreeDataTemplate)(templates.First());
Assert.IsType(template.ItemsSource);
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs
index ddeb558754..67e46d25c3 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs
@@ -24,7 +24,7 @@ namespace Avalonia.Markup.Xaml.UnitTests
[Fact]
public void Binding_Button_IsPressed_ShouldWork()
{
- var parsed = (Button)AvaloniaXamlLoader.Parse(@"
+ var parsed = (Button)AvaloniaRuntimeXamlLoader.Parse(@"
");
var ctx = new XamlIlBugTestsDataContext();
parsed.DataContext = ctx;
@@ -35,7 +35,7 @@ namespace Avalonia.Markup.Xaml.UnitTests
[Fact]
public void Transitions_Should_Be_Properly_Parsed()
{
- var parsed = (Grid)AvaloniaXamlLoader.Parse(@"
+ var parsed = (Grid)AvaloniaRuntimeXamlLoader.Parse(@"
@@ -55,7 +55,7 @@ namespace Avalonia.Markup.Xaml.UnitTests
var precompiled = new XamlIlClassWithPrecompiledXaml();
Assert.Equal(Brushes.Red, precompiled.Background);
Assert.Equal(1, precompiled.Opacity);
- var loaded = (XamlIlClassWithPrecompiledXaml)AvaloniaXamlLoader.Parse(@"
+ var loaded = (XamlIlClassWithPrecompiledXaml)AvaloniaRuntimeXamlLoader.Parse(@"
",
null, Application.Current);
- var parsed = (Window)AvaloniaXamlLoader.Parse(@"
+ var parsed = (Window)AvaloniaRuntimeXamlLoader.Parse(@"
new AvaloniaXamlLoader() {IsDesignMode = true}
+ AssertThrows(() => AvaloniaRuntimeXamlLoader
.Load(@"
", typeof(XamlIlTests).Assembly),
+ xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'/>", typeof(XamlIlTests).Assembly,
+ designMode: true),
e => e.Message.Contains("Unable to resolve ")
&& e.Message.Contains(" as static field, property, constant or enum value"));
@@ -200,14 +201,15 @@ namespace Avalonia.Markup.Xaml.UnitTests
{
SomeStaticProperty = "123";
- var loaded = (UserControl)new AvaloniaXamlLoader() {IsDesignMode = true}
+ var loaded = (UserControl)AvaloniaRuntimeXamlLoader
.Load(@"
", typeof(XamlIlTests).Assembly);
+ xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'/>", typeof(XamlIlTests).Assembly,
+ designMode: true);
Assert.Equal(Design.GetDataContext(loaded), SomeStaticProperty);
}
@@ -217,7 +219,7 @@ namespace Avalonia.Markup.Xaml.UnitTests
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
- var parsed = (Window)AvaloniaXamlLoader.Parse(@"
+ var parsed = (Window)AvaloniaRuntimeXamlLoader.Parse(@"
(@"
+ var parsed = AvaloniaRuntimeXamlLoader.Parse(@"
(@"
+ var parsed = AvaloniaRuntimeXamlLoader.Parse(@"
() == null)
+ AvaloniaLocator.CurrentMutable.Bind()
+ .ToConstant(new TestXamlLoaderShim());
+ }
+
+ class TestXamlLoaderShim : AvaloniaXamlLoader.IRuntimeXamlLoader
+ {
+ public object Load(Stream stream, Assembly localAsm, object o, Uri baseUri, bool designMode)
+ => AvaloniaRuntimeXamlLoader.Load(stream, localAsm, o, baseUri, designMode);
}
}
}
diff --git a/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj b/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj
index 32dc498781..d162a2abe0 100644
--- a/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj
+++ b/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj
@@ -8,7 +8,8 @@
+
-
+
diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs
index f1a1bd45ae..fda8503135 100644
--- a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs
+++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs
@@ -81,8 +81,7 @@ namespace Avalonia.ReactiveUI.UnitTests
private void InitializeComponent()
{
- var loader = new AvaloniaXamlLoader();
- loader.Load(@"
+ AvaloniaRuntimeXamlLoader.Load(@"
", null, this);
@@ -100,8 +99,7 @@ namespace Avalonia.ReactiveUI.UnitTests
private void InitializeComponent()
{
- var loader = new AvaloniaXamlLoader();
- loader.Load(@"
+ AvaloniaRuntimeXamlLoader.Load(@"
", null, this);
diff --git a/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj b/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj
index e0bd082fb3..584eaf6fa9 100644
--- a/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj
+++ b/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj
@@ -1,6 +1,7 @@
netcoreapp3.1
+ latest
diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs
index 43a791b2cb..bf41381b52 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs
@@ -490,10 +490,10 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
}
}
- [InlineData("0123456789\r0123456789", 2)]
- [InlineData("0123456789", 1)]
+ [InlineData("0123456789\r0123456789")]
+ [InlineData("0123456789")]
[Theory]
- public void Should_Include_Last_Line_When_Constraint_Is_Surpassed(string text, int numberOfLines)
+ public void Should_Include_First_Line_When_Constraint_Is_Surpassed(string text)
{
using (Start())
{
@@ -508,11 +508,11 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
Typeface.Default,
12,
Brushes.Black.ToImmutable(),
- maxHeight: lineHeight * numberOfLines - lineHeight * 0.5);
+ maxHeight: lineHeight - lineHeight * 0.5);
- Assert.Equal(numberOfLines, layout.TextLines.Count);
+ Assert.Equal(1, layout.TextLines.Count);
- Assert.Equal(numberOfLines * lineHeight, layout.Size.Height);
+ Assert.Equal(lineHeight, layout.Size.Height);
}
}
diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs
index ed00d6aaed..09cbf3bf08 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs
@@ -31,12 +31,37 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var nextCharacterHit = new CharacterHit(0);
- for (var i = 1; i < clusters.Length; i++)
+ for (var i = 0; i < clusters.Length; i++)
{
+ Assert.Equal(clusters[i], nextCharacterHit.FirstCharacterIndex);
+
nextCharacterHit = textLine.GetNextCaretCharacterHit(nextCharacterHit);
+ }
+
+ var lastCharacterHit = nextCharacterHit;
+
+ nextCharacterHit = textLine.GetNextCaretCharacterHit(lastCharacterHit);
+
+ Assert.Equal(lastCharacterHit.FirstCharacterIndex, nextCharacterHit.FirstCharacterIndex);
+
+ Assert.Equal(lastCharacterHit.TrailingLength, nextCharacterHit.TrailingLength);
+
+ nextCharacterHit = new CharacterHit(0, clusters[1] - clusters[0]);
+
+ for (var i = 0; i < clusters.Length; i++)
+ {
+ Assert.Equal(clusters[i], nextCharacterHit.FirstCharacterIndex);
- Assert.Equal(clusters[i], nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength);
+ nextCharacterHit = textLine.GetNextCaretCharacterHit(nextCharacterHit);
}
+
+ lastCharacterHit = nextCharacterHit;
+
+ nextCharacterHit = textLine.GetNextCaretCharacterHit(lastCharacterHit);
+
+ Assert.Equal(lastCharacterHit.FirstCharacterIndex, nextCharacterHit.FirstCharacterIndex);
+
+ Assert.Equal(lastCharacterHit.TrailingLength, nextCharacterHit.TrailingLength);
}
}
@@ -60,14 +85,41 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var clusters = textLine.TextRuns.Cast().SelectMany(x => x.GlyphRun.GlyphClusters)
.ToArray();
- var previousCharacterHit = new CharacterHit(clusters[^1]);
+ var previousCharacterHit = new CharacterHit(text.Length);
- for (var i = clusters.Length - 2; i > 0; i--)
+ for (var i = clusters.Length - 1; i >= 0; i--)
{
previousCharacterHit = textLine.GetPreviousCaretCharacterHit(previousCharacterHit);
- Assert.Equal(clusters[i], previousCharacterHit.FirstCharacterIndex);
+ Assert.Equal(clusters[i],
+ previousCharacterHit.FirstCharacterIndex + previousCharacterHit.TrailingLength);
}
+
+ var firstCharacterHit = previousCharacterHit;
+
+ previousCharacterHit = textLine.GetPreviousCaretCharacterHit(firstCharacterHit);
+
+ Assert.Equal(firstCharacterHit.FirstCharacterIndex, previousCharacterHit.FirstCharacterIndex);
+
+ Assert.Equal(firstCharacterHit.TrailingLength, previousCharacterHit.TrailingLength);
+
+ previousCharacterHit = new CharacterHit(clusters[^1], text.Length - clusters[^1]);
+
+ for (var i = clusters.Length - 1; i > 0; i--)
+ {
+ previousCharacterHit = textLine.GetPreviousCaretCharacterHit(previousCharacterHit);
+
+ Assert.Equal(clusters[i],
+ previousCharacterHit.FirstCharacterIndex + previousCharacterHit.TrailingLength);
+ }
+
+ firstCharacterHit = previousCharacterHit;
+
+ previousCharacterHit = textLine.GetPreviousCaretCharacterHit(firstCharacterHit);
+
+ Assert.Equal(firstCharacterHit.FirstCharacterIndex, previousCharacterHit.FirstCharacterIndex);
+
+ Assert.Equal(firstCharacterHit.TrailingLength, previousCharacterHit.TrailingLength);
}
}
@@ -162,6 +214,64 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
}
}
+ [InlineData("01234 01234", 8, TextCollapsingStyle.TrailingCharacter, "01234 0\u2026")]
+ [InlineData("01234 01234", 8, TextCollapsingStyle.TrailingWord, "01234 \u2026")]
+ [Theory]
+ public void Should_Collapse_Line(string text, int numberOfCharacters, TextCollapsingStyle style, string expected)
+ {
+ using (Start())
+ {
+ var defaultProperties = new GenericTextRunProperties(Typeface.Default);
+
+ var textSource = new SingleBufferTextSource(text, defaultProperties);
+
+ var formatter = new TextFormatterImpl();
+
+ var textLine =
+ formatter.FormatLine(textSource, 0, double.PositiveInfinity,
+ new GenericTextParagraphProperties(defaultProperties));
+
+ Assert.False(textLine.HasCollapsed);
+
+ var glyphTypeface = Typeface.Default.GlyphTypeface;
+
+ var scale = defaultProperties.FontRenderingEmSize / glyphTypeface.DesignEmHeight;
+
+ var width = 1.0;
+
+ for (var i = 0; i < numberOfCharacters; i++)
+ {
+ var glyph = glyphTypeface.GetGlyph(text[i]);
+
+ width += glyphTypeface.GetGlyphAdvance(glyph) * scale;
+ }
+
+ TextCollapsingProperties collapsingProperties;
+
+ if (style == TextCollapsingStyle.TrailingCharacter)
+ {
+ collapsingProperties = new TextTrailingCharacterEllipsis(width, defaultProperties);
+ }
+ else
+ {
+ collapsingProperties = new TextTrailingWordEllipsis(width, defaultProperties);
+ }
+
+ var collapsedLine = textLine.Collapse(collapsingProperties);
+
+ Assert.True(collapsedLine.HasCollapsed);
+
+ var trimmedText = collapsedLine.TextRuns.SelectMany(x => x.Text).ToArray();
+
+ Assert.Equal(expected.Length, trimmedText.Length);
+
+ for (var i = 0; i < expected.Length; i++)
+ {
+ Assert.Equal(expected[i], trimmedText[i]);
+ }
+ }
+ }
+
private static IDisposable Start()
{
var disposable = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface
diff --git a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs
index 48a333dc54..67503ef0d0 100644
--- a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs
+++ b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs
@@ -29,7 +29,8 @@ namespace Avalonia.UnitTests
windowImpl.SetupAllProperties();
windowImpl.Setup(x => x.ClientSize).Returns(() => clientSize);
windowImpl.Setup(x => x.MaxAutoSizeHint).Returns(s_screenSize);
- windowImpl.Setup(x => x.Scaling).Returns(1);
+ windowImpl.Setup(x => x.DesktopScaling).Returns(1);
+ windowImpl.Setup(x => x.RenderScaling).Returns(1);
windowImpl.Setup(x => x.Screen).Returns(CreateScreenMock().Object);
windowImpl.Setup(x => x.Position).Returns(() => position);
SetupToplevel(windowImpl);
@@ -81,7 +82,7 @@ namespace Avalonia.UnitTests
popupImpl.SetupAllProperties();
popupImpl.Setup(x => x.ClientSize).Returns(() => clientSize);
popupImpl.Setup(x => x.MaxAutoSizeHint).Returns(s_screenSize);
- popupImpl.Setup(x => x.Scaling).Returns(1);
+ popupImpl.Setup(x => x.RenderScaling).Returns(1);
popupImpl.Setup(x => x.PopupPositioner).Returns(positioner);
SetupToplevel(popupImpl);
diff --git a/tests/Avalonia.UnitTests/TestServices.cs b/tests/Avalonia.UnitTests/TestServices.cs
index cc06ea19f0..012cab23dc 100644
--- a/tests/Avalonia.UnitTests/TestServices.cs
+++ b/tests/Avalonia.UnitTests/TestServices.cs
@@ -167,8 +167,7 @@ namespace Avalonia.UnitTests
new DefaultTheme(),
};
- var loader = new AvaloniaXamlLoader();
- var baseLight = (IStyle)loader.Load(
+ var baseLight = (IStyle)AvaloniaXamlLoader.Load(
new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"));
result.Add(baseLight);
diff --git a/tests/Avalonia.Visuals.UnitTests/Media/ColorTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/ColorTests.cs
index f3f3c9a4ca..d68c2fd5fd 100644
--- a/tests/Avalonia.Visuals.UnitTests/Media/ColorTests.cs
+++ b/tests/Avalonia.Visuals.UnitTests/Media/ColorTests.cs
@@ -179,5 +179,25 @@ namespace Avalonia.Visuals.UnitTests.Media
{
Assert.False(Color.TryParse("#ff808g80", out _));
}
+
+ [Fact]
+ public void Parse_Throws_ArgumentNullException_For_Null_Input()
+ {
+ Assert.Throws(() => Color.Parse((string)null));
+ }
+
+ [Fact]
+ public void Parse_Throws_FormatException_For_Invalid_Input()
+ {
+ Assert.Throws(() => Color.Parse(string.Empty));
+ }
+
+ [Theory]
+ [InlineData("")]
+ [InlineData(null)]
+ public void TryParse_Returns_False_For_Invalid_Input(string input)
+ {
+ Assert.False(Color.TryParse(input, out _));
+ }
}
}