Browse Source

Merge branch 'master' into fixes/4293-listbox-remove-item-selection-2

pull/4308/head
danwalmsley 6 years ago
committed by GitHub
parent
commit
b8ff0459c1
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .gitmodules
  2. 27
      Avalonia.sln
  3. 1
      Directory.Build.props
  4. 2
      dirs.proj
  5. 16
      native/Avalonia.Native/src/OSX/Screens.mm
  6. 2
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  7. 10
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  8. 6
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  9. 16
      src/Avalonia.Controls/DateTimePickers/DatePicker.cs
  10. 31
      src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
  11. 2
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
  12. 1
      src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
  13. 6
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  14. 5
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  15. 21
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  16. 4
      src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs
  17. 2
      src/Avalonia.Controls/Repeater/ElementFactory.cs
  18. 1
      src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs
  19. 29
      src/Avalonia.Controls/Templates/FuncDataTemplate.cs
  20. 6
      src/Avalonia.Controls/Templates/FuncTemplate`2.cs
  21. 12
      src/Avalonia.Controls/Templates/IDataTemplate.cs
  22. 25
      src/Avalonia.Controls/Templates/IRecyclingDataTemplate.cs
  23. 2
      src/Avalonia.Controls/TextBox.cs
  24. 25
      src/Avalonia.Controls/TickBar.cs
  25. 4
      src/Avalonia.Controls/TopLevel.cs
  26. 2
      src/Avalonia.Controls/Window.cs
  27. 7
      src/Avalonia.DesignerSupport/DesignWindowLoader.cs
  28. 10
      src/Avalonia.DesignerSupport/Remote/FileWatcherTransport.cs
  29. 28
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json
  30. 1
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  31. 7
      src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
  32. 3
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  33. 2
      src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs
  34. 9
      src/Avalonia.Headless/HeadlessWindowImpl.cs
  35. 15
      src/Avalonia.Native/OsxManagedPopupPositionerPopupImplHelper.cs
  36. 2
      src/Avalonia.Native/PopupImpl.cs
  37. 8
      src/Avalonia.Native/WindowImplBase.cs
  38. 2
      src/Avalonia.Themes.Default/TextBox.xaml
  39. 107
      src/Avalonia.Themes.Fluent/TextBox.xaml
  40. 15
      src/Avalonia.Visuals/Media/Color.cs
  41. 15
      src/Avalonia.Visuals/Media/TextFormatting/GenericTextParagraphProperties.cs
  42. 3
      src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs
  43. 23
      src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs
  44. 18
      src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingStyle.cs
  45. 503
      src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
  46. 67
      src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
  47. 45
      src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs
  48. 157
      src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
  49. 11
      src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs
  50. 5
      src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs
  51. 7
      src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs
  52. 33
      src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs
  53. 37
      src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs
  54. 2
      src/Avalonia.Visuals/Media/TextFormatting/Unicode/Codepoint.cs
  55. 1
      src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs
  56. 16
      src/Avalonia.Visuals/Media/TextWrapping.cs
  57. 4
      src/Avalonia.X11/X11NativeControlHost.cs
  58. 47
      src/Avalonia.X11/X11Window.cs
  59. 4
      src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
  60. 14
      src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
  61. 42
      src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs
  62. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs
  63. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  64. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs
  65. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs
  66. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AddNameScopeRegistration.cs
  67. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaBindingExtensionTransformer.cs
  68. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs
  69. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs
  70. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs
  71. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlCompiledBindingsMetadataRemover.cs
  72. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs
  73. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs
  74. 29
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs
  75. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDesignPropertiesTransformer.cs
  76. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlMetadataRemover.cs
  77. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlPropertyPathTransformer.cs
  78. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlResolveByNameMarkupExtensionReplacer.cs
  79. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlRootObjectScopeTransformer.cs
  80. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs
  81. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs
  82. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs
  83. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs
  84. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransitionsTypeMetadataTransformer.cs
  85. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  86. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/IgnoredDirectivesTransformer.cs
  87. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/XNameTransformer.cs
  88. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs
  89. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs
  90. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs
  91. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlPropertyInfoAccessorFactoryEmitter.cs
  92. 12
      src/Markup/Avalonia.Markup.Xaml.Loader/IncludeXamlIlSre.props
  93. 0
      src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
  94. 36
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  95. 67
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  96. 3
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs
  97. 3
      src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
  98. 11
      src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
  99. 2
      src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
  100. 2
      src/Skia/Avalonia.Skia/FormattedTextImpl.cs

2
.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

27
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}

1
Directory.Build.props

@ -1,5 +1,6 @@
<Project>
<PropertyGroup>
<PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)build-intermediate/nuget</PackageOutputPath>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
</PropertyGroup>
</Project>

2
dirs.proj

@ -6,7 +6,7 @@
<ProjectReference Include="packages/**/*.*proj" />
<ProjectReference Remove="**/*.shproj" />
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/PortableXaml/**/*.*proj" />
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/XamlIl/**/*.*proj" />
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github/**/*.*proj" />
</ItemGroup>
<!--<ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\Android')">-->
<ItemGroup>

16
native/Avalonia.Native/src/OSX/Screens.mm

@ -4,6 +4,14 @@ class Screens : public ComSingleObject<IAvnScreens, &IID_IAvnScreens>
{
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<IAvnScreens, &IID_IAvnScreens>
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];

2
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()
{

10
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -17,14 +17,14 @@
<Compile Include="../Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaResourceXamlInfo.cs">
<Link>Shared/AvaloniaResourceXamlInfo.cs</Link>
</Compile>
<Compile Include="../Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/**/*.cs">
<Compile Include="../Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/**/*.cs">
<Link>XamlIlExtensions/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Remove="external/cecil/**/*.*" />
<Compile Include="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\src\XamlX\**\*.cs">
<Compile Include="../Markup/Avalonia.Markup.Xaml.Loader/xamlil.github\src\XamlX\**\*.cs">
<Link>XamlIl/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\src\XamlX.IL.Cecil\**\*.cs">
<Compile Include="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\src\XamlX.IL.Cecil\**\*.cs">
<Link>XamlIl.Cecil/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Markup/Avalonia.Markup\Markup\Parsers\SelectorGrammar.cs">
@ -57,8 +57,8 @@
<Compile Include="../Avalonia.Base/Utilities/StyleClassParser.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Remove="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\**\obj\**\*.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\src\XamlX\IL\SreTypeSystem.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\**\obj\**\*.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\src\XamlX\IL\SreTypeSystem.cs" />
<PackageReference Include="Avalonia.Unofficial.Cecil" Version="20190417.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.Build.Framework" Version="15.1.548" PrivateAssets="All" />
</ItemGroup>

6
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

16
src/Avalonia.Controls/DateTimePickers/DatePicker.cs

@ -88,7 +88,7 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterDirect<DatePicker, DateTimeOffset?>(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);

31
src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs

@ -77,7 +77,7 @@ namespace Avalonia.Controls
DatePicker.YearVisibleProperty.AddOwner<DatePickerPresenter>(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<Grid>("PickerContainer");
_monthHost = e.NameScope.Get<Panel>("MonthHost");
_dayHost = e.NameScope.Get<Panel>("DayHost");
@ -326,7 +326,7 @@ namespace Avalonia.Controls
/// </summary>
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;

2
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

1
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;
}

6
src/Avalonia.Controls/Platform/ITopLevelImpl.cs

@ -23,10 +23,10 @@ namespace Avalonia.Platform
Size ClientSize { get; }
/// <summary>
/// Gets the scaling factor for the toplevel.
/// Gets the scaling factor for the toplevel. This is used for rendering.
/// </summary>
double Scaling { get; }
double RenderScaling { get; }
/// <summary>
/// The list of native platform's surfaces that can be consumed by rendering subsystems.
/// </summary>

5
src/Avalonia.Controls/Platform/IWindowBaseImpl.cs

@ -13,6 +13,11 @@ namespace Avalonia.Platform
/// Hides the window.
/// </summary>
void Hide();
/// <summary>
/// Gets the scaling factor for Window positioning and sizing.
/// </summary>
double DesktopScaling { get; }
/// <summary>
/// Gets the position of the window in device pixels.

21
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();
/// <summary>
@ -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();

4
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;
}
}

2
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 });

1
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);

29
src/Avalonia.Controls/Templates/FuncDataTemplate.cs

@ -6,7 +6,7 @@ namespace Avalonia.Controls.Templates
/// <summary>
/// Builds a control for a piece of data.
/// </summary>
public class FuncDataTemplate : FuncTemplate<object, IControl>, IDataTemplate
public class FuncDataTemplate : FuncTemplate<object, IControl>, IRecyclingDataTemplate
{
/// <summary>
/// The default data template used in the case where no matching data template is found.
@ -30,10 +30,8 @@ namespace Avalonia.Controls.Templates
},
true);
/// <summary>
/// The implementation of the <see cref="Match"/> method.
/// </summary>
private readonly Func<object, bool> _match;
private readonly bool _supportsRecycling;
/// <summary>
/// Initializes a new instance of the <see cref="FuncDataTemplate"/> class.
@ -70,12 +68,9 @@ namespace Avalonia.Controls.Templates
Contract.Requires<ArgumentNullException>(match != null);
_match = match;
SupportsRecycling = supportsRecycling;
_supportsRecycling = supportsRecycling;
}
/// <inheritdoc/>
public bool SupportsRecycling { get; }
/// <summary>
/// Checks to see if this data template matches the specified data.
/// </summary>
@ -88,6 +83,24 @@ namespace Avalonia.Controls.Templates
return _match(data);
}
/// <summary>
/// Creates or recycles a control to display the specified data.
/// </summary>
/// <param name="data">The data to display.</param>
/// <param name="existing">An optional control to recycle.</param>
/// <returns>
/// The <paramref name="existing"/> control if supplied and applicable to
/// <paramref name="data"/>, otherwise a new control.
/// </returns>
/// <remarks>
/// The caller should ensure that any control passed to <paramref name="existing"/>
/// originated from the same data template.
/// </remarks>
public IControl Build(object data, IControl existing)
{
return _supportsRecycling && existing is object ? existing : Build(data);
}
/// <summary>
/// Determines of an object is of the specified type.
/// </summary>

6
src/Avalonia.Controls/Templates/FuncTemplate`2.cs

@ -1,5 +1,7 @@
using System;
#nullable enable
namespace Avalonia.Controls.Templates
{
/// <summary>
@ -18,9 +20,7 @@ namespace Avalonia.Controls.Templates
/// <param name="func">The function used to create the control.</param>
public FuncTemplate(Func<TParam, INameScope, TControl> func)
{
Contract.Requires<ArgumentNullException>(func != null);
_func = func;
_func = func ?? throw new ArgumentNullException(nameof(func));
}
/// <summary>

12
src/Avalonia.Controls/Templates/IDataTemplate.cs

@ -1,3 +1,7 @@
using System;
#nullable enable
namespace Avalonia.Controls.Templates
{
/// <summary>
@ -5,12 +9,6 @@ namespace Avalonia.Controls.Templates
/// </summary>
public interface IDataTemplate : ITemplate<object, IControl>
{
/// <summary>
/// Gets a value indicating whether the data template supports recycling of the generated
/// control.
/// </summary>
bool SupportsRecycling { get; }
/// <summary>
/// Checks to see if this data template matches the specified data.
/// </summary>
@ -20,4 +18,4 @@ namespace Avalonia.Controls.Templates
/// </returns>
bool Match(object data);
}
}
}

25
src/Avalonia.Controls/Templates/IRecyclingDataTemplate.cs

@ -0,0 +1,25 @@
#nullable enable
namespace Avalonia.Controls.Templates
{
/// <summary>
/// An <see cref="IDataTemplate"/> that supports recycling existing elements.
/// </summary>
public interface IRecyclingDataTemplate : IDataTemplate
{
/// <summary>
/// Creates or recycles a control to display the specified data.
/// </summary>
/// <param name="data">The data to display.</param>
/// <param name="existing">An optional control to recycle.</param>
/// <returns>
/// The <paramref name="existing"/> control if supplied and applicable to
/// <paramref name="data"/>, otherwise a new control.
/// </returns>
/// <remarks>
/// The caller should ensure that any control passed to <paramref name="existing"/>
/// originated from the same data template.
/// </remarks>
IControl Build(object data, IControl? existing);
}
}

2
src/Avalonia.Controls/TextBox.cs

@ -134,7 +134,7 @@ namespace Avalonia.Controls
{
if (acceptsReturn)
{
return wrapping == TextWrapping.NoWrap ?
return wrapping != TextWrapping.Wrap ?
ScrollBarVisibility.Auto :
ScrollBarVisibility.Disabled;
}

25
src/Avalonia.Controls/TickBar.cs

@ -39,11 +39,15 @@ namespace Avalonia.Controls
{
static TickBar()
{
AffectsRender<TickBar>(ReservedSpaceProperty,
AffectsRender<TickBar>(FillProperty,
IsDirectionReversedProperty,
ReservedSpaceProperty,
MaximumProperty,
MinimumProperty,
OrientationProperty,
TickFrequencyProperty);
PlacementProperty,
TickFrequencyProperty,
TicksProperty);
}
public TickBar() : base()
@ -137,7 +141,7 @@ namespace Avalonia.Controls
/// <summary>
/// The Ticks property contains collection of value of type Double which
/// are the logical positions use to draw the ticks.
/// The property value is a <see cref="DoubleCollection" />.
/// The property value is a <see cref="AvaloniaList{T}" />.
/// </summary>
public AvaloniaList<double> Ticks
{
@ -169,7 +173,6 @@ namespace Avalonia.Controls
public static readonly StyledProperty<TickBarPlacement> PlacementProperty =
AvaloniaProperty.Register<TickBar, TickBarPlacement>(nameof(Placement), 0d);
/// <summary>
/// Placement property specified how the Tick will be placed.
/// This property affects the way ticks are drawn.
@ -189,7 +192,7 @@ namespace Avalonia.Controls
/// <summary>
/// TickBar will use ReservedSpaceProperty for left and right spacing (for horizontal orientation) or
/// tob and bottom spacing (for vertical orienation).
/// top and bottom spacing (for vertical orienation).
/// The space on both sides of TickBar is half of specified ReservedSpace.
/// This property has type of <see cref="Rect" />.
/// </summary>
@ -201,7 +204,7 @@ namespace Avalonia.Controls
/// <summary>
/// Draw ticks.
/// Ticks can be draw in 8 diffrent ways depends on Placment property and IsDirectionReversed property.
/// Ticks can be draw in 8 different ways depends on Placement property and IsDirectionReversed property.
///
/// This function also draw selection-tick(s) if IsSelectionRangeEnabled is 'true' and
/// SelectionStart and SelectionEnd are valid.
@ -211,9 +214,7 @@ namespace Avalonia.Controls
///
/// The secondary ticks (all other ticks, including selection-tics) height will be 75% of TickBar's render size.
///
/// Brush that use to fill ticks is specified by Shape.Fill property.
///
/// Pen that use to draw ticks is specified by Shape.Pen property.
/// Brush that use to fill ticks is specified by Fill property.
/// </summary>
public override void Render(DrawingContext dc)
{
@ -222,7 +223,6 @@ namespace Avalonia.Controls
var tickLen = 0.0d; // Height for Primary Tick (for Mininum and Maximum value)
var tickLen2 = 0.0d; // Height for Secondary Tick
var logicalToPhysical = 1.0;
var progression = 1.0d;
var startPoint = new Point();
var endPoint = new Point();
var rSpace = Orientation == Orientation.Horizontal ? ReservedSpace.Width : ReservedSpace.Height;
@ -242,7 +242,6 @@ namespace Avalonia.Controls
startPoint = new Point(halfReservedSpace, size.Height);
endPoint = new Point(halfReservedSpace + size.Width, size.Height);
logicalToPhysical = size.Width / range;
progression = 1;
break;
case TickBarPlacement.Bottom:
@ -255,7 +254,6 @@ namespace Avalonia.Controls
startPoint = new Point(halfReservedSpace, 0d);
endPoint = new Point(halfReservedSpace + size.Width, 0d);
logicalToPhysical = size.Width / range;
progression = 1;
break;
case TickBarPlacement.Left:
@ -269,7 +267,6 @@ namespace Avalonia.Controls
startPoint = new Point(size.Width, size.Height + halfReservedSpace);
endPoint = new Point(size.Width, halfReservedSpace);
logicalToPhysical = size.Height / range * -1;
progression = -1;
break;
case TickBarPlacement.Right:
@ -282,7 +279,6 @@ namespace Avalonia.Controls
startPoint = new Point(0d, size.Height + halfReservedSpace);
endPoint = new Point(0d, halfReservedSpace);
logicalToPhysical = size.Height / range * -1;
progression = -1;
break;
};
@ -291,7 +287,6 @@ namespace Avalonia.Controls
// Invert direciton of the ticks
if (IsDirectionReversed)
{
progression *= -progression;
logicalToPhysical *= -1;
// swap startPoint & endPoint

4
src/Avalonia.Controls/TopLevel.cs

@ -280,10 +280,10 @@ namespace Avalonia.Controls
}
/// <inheritdoc/>
double ILayoutRoot.LayoutScaling => PlatformImpl?.Scaling ?? 1;
double ILayoutRoot.LayoutScaling => PlatformImpl?.RenderScaling ?? 1;
/// <inheritdoc/>
double IRenderRoot.RenderScaling => PlatformImpl?.Scaling ?? 1;
double IRenderRoot.RenderScaling => PlatformImpl?.RenderScaling ?? 1;
IStyleHost IStyleHost.StylingParent => _globalStyles;

2
src/Avalonia.Controls/Window.cs

@ -818,7 +818,7 @@ namespace Avalonia.Controls
private void SetWindowStartupLocation(IWindowBaseImpl owner = null)
{
var scaling = owner?.Scaling ?? PlatformImpl?.Scaling ?? 1;
var scaling = owner?.DesktopScaling ?? PlatformImpl?.DesktopScaling ?? 1;
// TODO: We really need non-client size here.
var rect = new PixelRect(

7
src/Avalonia.DesignerSupport/DesignWindowLoader.cs

@ -18,10 +18,11 @@ namespace Avalonia.DesignerSupport
Control control;
using (PlatformManager.DesignerMode())
{
var loader = new AvaloniaXamlLoader() {IsDesignMode = true};
var loader = AvaloniaLocator.Current.GetService<AvaloniaXamlLoader.IRuntimeXamlLoader>();
var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml));
if (loader == null)
throw new XamlLoadException("Runtime XAML loader is not registered");
Uri baseUri = null;
if (assemblyPath != null)
@ -34,7 +35,7 @@ namespace Avalonia.DesignerSupport
}
var localAsm = assemblyPath != null ? Assembly.LoadFile(Path.GetFullPath(assemblyPath)) : null;
var loaded = loader.Load(stream, localAsm, null, baseUri);
var loaded = loader.Load(stream, localAsm, null, baseUri, true);
var style = loaded as IStyle;
if (style != null)
{

10
src/Avalonia.DesignerSupport/Remote/FileWatcherTransport.cs

@ -9,12 +9,14 @@ namespace Avalonia.DesignerSupport.Remote
{
class FileWatcherTransport : IAvaloniaRemoteTransportConnection, ITransportWithEnforcedMethod
{
private readonly string _appPath;
private string _path;
private string _lastContents;
private bool _disposed;
public FileWatcherTransport(Uri file)
public FileWatcherTransport(Uri file, string appPath)
{
_appPath = appPath;
_path = file.LocalPath;
}
@ -73,7 +75,11 @@ namespace Avalonia.DesignerSupport.Remote
{
Console.WriteLine("Triggering XAML update");
_lastContents = data;
_onMessage?.Invoke(this, new UpdateXamlMessage { Xaml = data });
_onMessage?.Invoke(this, new UpdateXamlMessage
{
Xaml = data,
AssemblyPath = _appPath
});
}
await Task.Delay(100);

28
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json

@ -3564,12 +3564,14 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -3584,17 +3586,20 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@ -3711,7 +3716,8 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@ -3723,6 +3729,7 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -3737,6 +3744,7 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@ -3744,12 +3752,14 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@ -3768,6 +3778,7 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -3848,7 +3859,8 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@ -3860,6 +3872,7 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -3981,6 +3994,7 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",

1
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@ -36,6 +36,7 @@ namespace Avalonia.DesignerSupport.Remote
{
}
public double DesktopScaling => 1.0;
public PixelPoint Position { get; set; }
public Action<PixelPoint> PositionChanged { get; set; }
public Action Deactivated { get; set; }

7
src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs

@ -112,8 +112,9 @@ namespace Avalonia.DesignerSupport.Remote
return rv;
}
static IAvaloniaRemoteTransportConnection CreateTransport(Uri transport)
static IAvaloniaRemoteTransportConnection CreateTransport(CommandLineArgs args)
{
var transport = args.Transport;
if (transport.Scheme == "tcp-bson")
{
return new BsonTcpTransport().Connect(IPAddress.Parse(transport.Host), transport.Port).Result;
@ -121,7 +122,7 @@ namespace Avalonia.DesignerSupport.Remote
if (transport.Scheme == "file")
{
return new FileWatcherTransport(transport);
return new FileWatcherTransport(transport, args.AppPath);
}
PrintUsage();
return null;
@ -160,7 +161,7 @@ namespace Avalonia.DesignerSupport.Remote
public static void Main(string[] cmdline)
{
var args = ParseCommandLineArgs(cmdline);
var transport = CreateTransport(args.Transport);
var transport = CreateTransport(args);
if (transport is ITransportWithEnforcedMethod enforcedMethod)
args.Method = enforcedMethod.PreviewerMethod;
var asm = Assembly.LoadFile(System.IO.Path.GetFullPath(args.AppPath));

3
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -21,7 +21,8 @@ namespace Avalonia.DesignerSupport.Remote
public IPlatformHandle Handle { get; }
public Size MaxAutoSizeHint { get; }
public Size ClientSize { get; }
public double Scaling { get; } = 1.0;
public double RenderScaling { get; } = 1.0;
public double DesktopScaling => 1.0;
public IEnumerable<object> Surfaces { get; }
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; }

2
src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs

@ -7,8 +7,6 @@ namespace Avalonia.Diagnostics
{
internal class ViewLocator : IDataTemplate
{
public bool SupportsRecycling => false;
public IControl Build(object data)
{
var name = data.GetType().FullName.Replace("ViewModel", "View");

9
src/Avalonia.Headless/HeadlessWindowImpl.cs

@ -41,7 +41,8 @@ namespace Avalonia.Headless
}
public Size ClientSize { get; set; }
public double Scaling { get; } = 1;
public double RenderScaling { get; } = 1;
public double DesktopScaling => RenderScaling;
public IEnumerable<object> Surfaces { get; }
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; }
@ -62,9 +63,9 @@ namespace Avalonia.Headless
public IInputRoot InputRoot { get; set; }
public Point PointToClient(PixelPoint point) => point.ToPoint(Scaling);
public Point PointToClient(PixelPoint point) => point.ToPoint(RenderScaling);
public PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, Scaling);
public PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, RenderScaling);
public void SetCursor(IPlatformHandle cursor)
{
@ -201,7 +202,7 @@ namespace Avalonia.Headless
public ILockedFramebuffer Lock()
{
var bmp = new WriteableBitmap(PixelSize.FromSize(ClientSize, Scaling), new Vector(96, 96) * Scaling);
var bmp = new WriteableBitmap(PixelSize.FromSize(ClientSize, RenderScaling), new Vector(96, 96) * RenderScaling);
var fb = bmp.Lock();
return new FramebufferProxy(fb, () =>
{

15
src/Avalonia.Native/OsxManagedPopupPositionerPopupImplHelper.cs

@ -1,15 +0,0 @@
using Avalonia.Controls.Primitives.PopupPositioning;
using Avalonia.Platform;
namespace Avalonia.Native
{
class OsxManagedPopupPositionerPopupImplHelper : ManagedPopupPositionerPopupImplHelper
{
public OsxManagedPopupPositionerPopupImplHelper(IWindowBaseImpl parent, MoveResizeDelegate moveResize) : base(parent, moveResize)
{
}
public override double Scaling => 1;
}
}

2
src/Avalonia.Native/PopupImpl.cs

@ -26,7 +26,7 @@ namespace Avalonia.Native
var context = _opts.UseGpu ? glFeature?.DeferredContext : null;
Init(factory.CreatePopup(e, context?.Context), factory.CreateScreens(), context);
}
PopupPositioner = new ManagedPopupPositioner(new OsxManagedPopupPositionerPopupImplHelper(parent, MoveResize));
PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize));
}
private void MoveResize(PixelPoint position, Size size, double scaling)

8
src/Avalonia.Native/WindowImplBase.cs

@ -81,7 +81,7 @@ namespace Avalonia.Native
_glSurface = new GlPlatformSurface(window, _glContext);
Screen = new ScreenImpl(screens);
_savedLogicalSize = ClientSize;
_savedScaling = Scaling;
_savedScaling = RenderScaling;
_nativeControlHost = new NativeControlHostImpl(_native.CreateNativeControlHost());
var monitor = Screen.AllScreens.OrderBy(x => x.PixelDensity)
@ -369,7 +369,9 @@ namespace Avalonia.Native
_native.SetTopMost(value);
}
public double Scaling => _native?.GetScaling() ?? 1;
public double RenderScaling => _native?.GetScaling() ?? 1;
public double DesktopScaling => 1;
public Action Deactivated { get; set; }
public Action Activated { get; set; }
@ -432,7 +434,7 @@ namespace Avalonia.Native
TransparencyLevel = transparencyLevel;
_native.SetBlurEnabled(TransparencyLevel >= WindowTransparencyLevel.Blur);
_native?.SetBlurEnabled(TransparencyLevel >= WindowTransparencyLevel.Blur);
TransparencyLevelChanged?.Invoke(TransparencyLevel);
}
}

2
src/Avalonia.Themes.Default/TextBox.xaml

@ -40,6 +40,8 @@
<TextBlock Name="watermark"
Opacity="0.5"
Text="{TemplateBinding Watermark}"
TextAlignment="{TemplateBinding TextAlignment}"
TextWrapping="{TemplateBinding TextWrapping}"
IsVisible="{TemplateBinding Text, Converter={x:Static StringConverters.IsNullOrEmpty}}"/>
<TextPresenter Name="PART_TextPresenter"
Text="{TemplateBinding Text, Mode=TwoWay}"

107
src/Avalonia.Themes.Fluent/TextBox.xaml

@ -10,71 +10,65 @@
<Setter Property="SelectionBrush" Value="{DynamicResource TextControlSelectionHighlightColor}" />
<Setter Property="BorderThickness" Value="{DynamicResource TextControlBorderThemeThickness}" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden" />
<Setter Property="MinHeight" Value="{DynamicResource TextControlThemeMinHeight}" />
<Setter Property="MinWidth" Value="{DynamicResource TextControlThemeMinWidth}" />
<Setter Property="Padding" Value="{DynamicResource TextControlThemePadding}" />
<Setter Property="Template">
<ControlTemplate>
<Grid RowDefinitions="Auto, *, Auto" ColumnDefinitions="*, Auto">
<DockPanel>
<!-- TODO bind Content -> Header and ContentTemplate -> HeaderTemplate -->
<ContentPresenter x:Name="HeaderContentPresenter"
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
DockPanel.Dock="Top"
TextBlock.FontWeight="Normal"
TextBlock.Foreground="{DynamicResource TextControlHeaderForeground}"
IsVisible="False"
Margin="{DynamicResource TextBoxTopHeaderMargin}" />
<Border Name="border"
Grid.Row="1"
Grid.Column="0"
Grid.RowSpan="1"
Grid.ColumnSpan="2"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{DynamicResource ControlCornerRadius}"
Margin="{TemplateBinding BorderThickness}"
MinWidth="{TemplateBinding MinWidth}"
MinHeight="{TemplateBinding MinHeight}">
</Border>
<Border Padding="{TemplateBinding Padding}"
Grid.Row="1"
Grid.Column="0"
Margin="{TemplateBinding BorderThickness}">
<DockPanel>
<TextBlock Name="floatingWatermark"
Foreground="{DynamicResource SystemAccentColor}"
FontSize="{DynamicResource FontSizeSmall}"
Text="{TemplateBinding Watermark}"
DockPanel.Dock="Top">
<TextBlock.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="UseFloatingWatermark"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="Text"
Converter="{x:Static StringConverters.IsNotNullOrEmpty}"/>
</MultiBinding>
</TextBlock.IsVisible>
</TextBlock>
<DataValidationErrors>
<ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
<Panel>
<TextBlock Name="watermark"
<Panel>
<Border
Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{DynamicResource ControlCornerRadius}"
MinWidth="{TemplateBinding MinWidth}"
MinHeight="{TemplateBinding MinHeight}">
</Border>
<Border
Padding="{TemplateBinding Padding}"
Margin="{TemplateBinding BorderThickness}">
<DockPanel>
<TextBlock Name="floatingWatermark"
Foreground="{DynamicResource SystemAccentColor}"
FontSize="{DynamicResource FontSizeSmall}"
Text="{TemplateBinding Watermark}"
DockPanel.Dock="Top">
<TextBlock.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="UseFloatingWatermark"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="Text"
Converter="{x:Static StringConverters.IsNotNullOrEmpty}"/>
</MultiBinding>
</TextBlock.IsVisible>
</TextBlock>
<DataValidationErrors>
<ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
<Panel>
<TextBlock Name="watermark"
Opacity="0.5"
Text="{TemplateBinding Watermark}"
TextAlignment="{TemplateBinding TextAlignment}"
TextWrapping="{TemplateBinding TextWrapping}"
IsVisible="{TemplateBinding Text, Converter={x:Static StringConverters.IsNullOrEmpty}}"/>
<!-- TODO eliminate this margin... text layout issue? -->
<TextPresenter Name="PART_TextPresenter"
<!-- TODO eliminate this margin... text layout issue? -->
<TextPresenter Name="PART_TextPresenter"
Margin="0 1 0 0"
Text="{TemplateBinding Text, Mode=TwoWay}"
CaretIndex="{TemplateBinding CaretIndex}"
@ -86,12 +80,13 @@
SelectionBrush="{TemplateBinding SelectionBrush}"
SelectionForegroundBrush="{TemplateBinding SelectionForegroundBrush}"
CaretBrush="{TemplateBinding CaretBrush}"/>
</Panel>
</ScrollViewer>
</DataValidationErrors>
</DockPanel>
</Border>
</Grid>
</Panel>
</ScrollViewer>
</DataValidationErrors>
</DockPanel>
</Border>
</Panel>
</DockPanel>
</ControlTemplate>
</Setter>
</Style>

15
src/Avalonia.Visuals/Media/Color.cs

@ -89,6 +89,11 @@ namespace Avalonia.Media
/// <returns>The <see cref="Color"/>.</returns>
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
/// <returns>The status of the operation.</returns>
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;
}

15
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;
/// <summary>
@ -50,13 +44,6 @@
{
_textWrapping = textWrapping;
}
/// <summary>
/// Set text trimming
/// </summary>
internal void SetTextTrimming(TextTrimming textTrimming)
{
_textTrimming = textTrimming;
}
/// <summary>
/// Set line height

3
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
{

23
src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs

@ -0,0 +1,23 @@
namespace Avalonia.Media.TextFormatting
{
/// <summary>
/// Properties of text collapsing
/// </summary>
public abstract class TextCollapsingProperties
{
/// <summary>
/// Gets the width in which the collapsible range is constrained to
/// </summary>
public abstract double Width { get; }
/// <summary>
/// Gets the text run that is used as collapsing symbol
/// </summary>
public abstract TextRun Symbol { get; }
/// <summary>
/// Gets the style of collapsing
/// </summary>
public abstract TextCollapsingStyle Style { get; }
}
}

18
src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingStyle.cs

@ -0,0 +1,18 @@
namespace Avalonia.Media.TextFormatting
{
/// <summary>
/// Text collapsing style
/// </summary>
public enum TextCollapsingStyle
{
/// <summary>
/// Collapse trailing characters
/// </summary>
TrailingCharacter,
/// <summary>
/// Collapse trailing words
/// </summary>
TrailingWord,
}
}

503
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<char> s_ellipsis = new ReadOnlySlice<char>(new[] { '\u2026' });
/// <inheritdoc cref="TextFormatter.FormatLine"/>
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;
}
/// <summary>
/// Measures the number of characters that fits into available width.
/// </summary>
/// <param name="textCharacters">The text run.</param>
/// <param name="availableWidth">The available width.</param>
/// <returns></returns>
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;
}
/// <summary>
/// Split a sequence of runs into two segments at specified length.
/// </summary>
/// <param name="textRuns">The text run's.</param>
/// <param name="length">The length to split at.</param>
/// <returns>The split text runs.</returns>
internal static SplitTextRunsResult SplitTextRuns(IReadOnlyList<ShapedTextCharacters> 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);
}
/// <summary>
@ -174,87 +316,6 @@ namespace Avalonia.Media.TextFormatting
return false;
}
/// <summary>
/// Performs text trimming and returns a trimmed line.
/// </summary>
/// <param name="textRuns">The text runs to perform the trimming on.</param>
/// <param name="textRange">The text range that is covered by the text runs.</param>
/// <param name="paragraphWidth">A <see cref="double"/> value that specifies the width of the paragraph that the line fills.</param>
/// <param name="paragraphProperties">A <see cref="TextParagraphProperties"/> value that represents paragraph properties,
/// such as TextWrapping, TextAlignment, or TextStyle.</param>
/// <returns></returns>
private static TextLine PerformTextTrimming(IReadOnlyList<ShapedTextCharacters> 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<ShapedTextCharacters>(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));
}
/// <summary>
/// Performs text wrapping returns a list of text lines.
/// </summary>
@ -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));
}
/// <summary>
/// Measures the number of characters that fits into available width.
/// </summary>
/// <param name="textCharacters">The text run.</param>
/// <param name="availableWidth">The available width.</param>
/// <returns></returns>
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;
}
/// <summary>
/// Creates an ellipsis.
/// </summary>
/// <param name="properties">The text run properties.</param>
/// <returns></returns>
private static ShapedTextCharacters CreateEllipsisRun(TextRunProperties properties)
{
var formatterImpl = AvaloniaLocator.Current.GetService<ITextShaperImpl>();
var glyphRun = formatterImpl.ShapeText(s_ellipsis, properties.Typeface, properties.FontRenderingEmSize,
properties.CultureInfo);
return new ShapedTextCharacters(glyphRun, properties);
}
/// <summary>
/// Gets the text range that is covered by the text runs.
/// </summary>
@ -464,86 +432,7 @@ namespace Avalonia.Media.TextFormatting
return new TextRange(start, end - start);
}
/// <summary>
/// Split a sequence of runs into two segments at specified length.
/// </summary>
/// <param name="textRuns">The text run's.</param>
/// <param name="length">The length to split at.</param>
/// <returns>The split text runs.</returns>
private static SplitTextRunsResult SplitTextRuns(IReadOnlyList<ShapedTextCharacters> 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<ShapedTextCharacters> first, IReadOnlyList<ShapedTextCharacters> second)
{

67
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<char> _text;
private readonly TextParagraphProperties _paragraphProperties;
private readonly IReadOnlyList<ValueSpan<TextRunProperties>> _textStyleOverrides;
private readonly TextTrimming _textTrimming;
/// <summary>
/// Initializes a new instance of the <see cref="TextLayout" /> class.
@ -54,9 +53,11 @@ namespace Avalonia.Media.TextFormatting
new ReadOnlySlice<char>(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
/// <param name="foreground">The foreground.</param>
/// <param name="textAlignment">The text alignment.</param>
/// <param name="textWrapping">The text wrapping.</param>
/// <param name="textTrimming">The text trimming.</param>
/// <param name="textDecorations">The text decorations.</param>
/// <param name="lineHeight">The height of each line of text.</param>
/// <returns></returns>
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);
}
/// <summary>
@ -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
}
}
/// <summary>
/// Gets the <see cref="TextCollapsingProperties"/> for current text trimming mode.
/// </summary>
/// <param name="width">The collapsing width.</param>
/// <returns>The <see cref="TextCollapsingProperties"/>.</returns>
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<char> _text;

45
src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs

@ -39,6 +39,14 @@ namespace Avalonia.Media.TextFormatting
/// </returns>
public abstract TextLineBreak LineBreak { get; }
/// <summary>
/// Gets a value that indicates whether the line is collapsed.
/// </summary>
/// <returns>
/// <c>true</c>, if the line is collapsed; otherwise, <c>false</c>.
/// </returns>
public abstract bool HasCollapsed { get; }
/// <summary>
/// Draws the <see cref="TextLine"/> at the given origin.
/// </summary>
@ -47,40 +55,49 @@ namespace Avalonia.Media.TextFormatting
public abstract void Draw(DrawingContext drawingContext, Point origin);
/// <summary>
/// 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.
/// </summary>
/// <param name="collapsingPropertiesList">A list of <see cref="TextCollapsingProperties"/>
/// objects that represent the collapsed text properties.</param>
/// <returns>
/// A <see cref="TextLine"/> value that represents a collapsed line that can be displayed.
/// </returns>
public abstract TextLine Collapse(params TextCollapsingProperties[] collapsingPropertiesList);
/// <summary>
/// Gets the character hit corresponding to the specified distance from the beginning of the line.
/// </summary>
/// <param name="distance">distance in text flow direction from the beginning of the line</param>
/// <returns>The <see cref="CharacterHit"/></returns>
/// <param name="distance">A <see cref="double"/> value that represents the distance from the beginning of the line.</param>
/// <returns>The <see cref="CharacterHit"/> object at the specified distance from the beginning of the line.</returns>
public abstract CharacterHit GetCharacterHitFromDistance(double distance);
/// <summary>
/// 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.
/// <see cref="CharacterHit"/>.
/// </summary>
/// <param name="characterHit"><see cref="CharacterHit"/> of the character to query the distance.</param>
/// <returns>Distance in text flow direction from the beginning of the line.</returns>
/// <param name="characterHit">The <see cref="CharacterHit"/> object whose distance you want to query.</param>
/// <returns>A <see cref="double"/> that represents the distance from the beginning of the line.</returns>
public abstract double GetDistanceFromCharacterHit(CharacterHit characterHit);
/// <summary>
/// Client to get the next <see cref="CharacterHit"/> for caret navigation.
/// Gets the next character hit for caret navigation.
/// </summary>
/// <param name="characterHit">The current <see cref="CharacterHit"/>.</param>
/// <returns>The next <see cref="CharacterHit"/>.</returns>
public abstract CharacterHit GetNextCaretCharacterHit(CharacterHit characterHit);
/// <summary>
/// Client to get the previous character hit for caret navigation
/// Gets the previous character hit for caret navigation.
/// </summary>
/// <param name="characterHit">the current character hit</param>
/// <returns>The previous <see cref="CharacterHit"/></returns>
/// <param name="characterHit">The current <see cref="CharacterHit"/>.</param>
/// <returns>The previous <see cref="CharacterHit"/>.</returns>
public abstract CharacterHit GetPreviousCaretCharacterHit(CharacterHit characterHit);
/// <summary>
/// Client to get the previous character hit after backspacing
/// Gets the previous character hit after backspacing.
/// </summary>
/// <param name="characterHit">the current character hit</param>
/// <returns>The <see cref="CharacterHit"/> after backspacing</returns>
/// <param name="characterHit">The current <see cref="CharacterHit"/>.</param>
/// <returns>The <see cref="CharacterHit"/> after backspacing.</returns>
public abstract CharacterHit GetBackspaceCaretCharacterHit(CharacterHit characterHit);
/// <summary>

157
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<ShapedTextCharacters> _textRuns;
public TextLineImpl(IReadOnlyList<ShapedTextCharacters> textRuns, TextLineMetrics lineMetrics,
TextLineBreak lineBreak = null)
TextLineBreak lineBreak = null, bool hasCollapsed = false)
{
_textRuns = textRuns;
LineMetrics = lineMetrics;
LineBreak = lineBreak;
HasCollapsed = hasCollapsed;
}
/// <inheritdoc/>
@ -26,6 +29,9 @@ namespace Avalonia.Media.TextFormatting
/// <inheritdoc/>
public override TextLineBreak LineBreak { get; }
/// <inheritdoc/>
public override bool HasCollapsed { get; }
/// <inheritdoc/>
public override void Draw(DrawingContext drawingContext, Point origin)
{
@ -41,6 +47,99 @@ namespace Avalonia.Media.TextFormatting
}
}
/// <inheritdoc/>
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<ShapedTextCharacters>(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<ShapedTextCharacters>(_textRuns) { shapedSymbol }, textLineMetrics, null,
true);
}
/// <inheritdoc/>
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
}
/// <inheritdoc/>
@ -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
}
/// <inheritdoc/>
@ -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;
}
/// <summary>
/// Creates a shaped symbol.
/// </summary>
/// <param name="textRun">The symbol run to shape.</param>
/// <returns>
/// The shaped symbol.
/// </returns>
internal static ShapedTextCharacters CreateShapedSymbol(TextRun textRun)
{
var formatterImpl = AvaloniaLocator.Current.GetService<ITextShaperImpl>();
var glyphRun = formatterImpl.ShapeText(textRun.Text, textRun.Properties.Typeface, textRun.Properties.FontRenderingEmSize,
textRun.Properties.CultureInfo);
return new ShapedTextCharacters(glyphRun, textRun.Properties);
}
/// <summary>
/// Gets the shaped width of specified shaped text characters.
/// </summary>
/// <param name="shapedTextCharacters">The shaped text characters.</param>
/// <returns>
/// The shaped width.
/// </returns>
private static double GetShapedWidth(IReadOnlyList<ShapedTextCharacters> shapedTextCharacters)
{
var shapedWidth = 0.0;
for (var i = 0; i < shapedTextCharacters.Count; i++)
{
shapedWidth += shapedTextCharacters[i].Bounds.Width;
}
return shapedWidth;
}
}
}

11
src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs

@ -9,11 +9,12 @@ namespace Avalonia.Media.TextFormatting
/// </summary>
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;
}
/// <summary>
@ -37,6 +38,12 @@ namespace Avalonia.Media.TextFormatting
/// </summary>
public double TextBaseline { get; }
/// <summary>
/// Gets a boolean value that indicates whether content of the line overflows
/// the specified paragraph width.
/// </summary>
public bool HasOverflowed { get; }
/// <summary>
/// Creates the text line metrics.
/// </summary>
@ -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);
}
}
}

5
src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs

@ -26,11 +26,6 @@
/// </summary>
public abstract TextWrapping TextWrapping { get; }
/// <summary>
/// Gets the text trimming.
/// </summary>
public abstract TextTrimming TextTrimming { get; }
/// <summary>
/// Paragraph's line height
/// </summary>

7
src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs

@ -4,12 +4,11 @@ using System.Globalization;
namespace Avalonia.Media.TextFormatting
{
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public abstract class TextRunProperties : IEquatable<TextRunProperties>
{

33
src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs

@ -0,0 +1,33 @@
using Avalonia.Utilities;
namespace Avalonia.Media.TextFormatting
{
/// <summary>
/// a collapsing properties to collapse whole line toward the end
/// at character granularity and with ellipsis being the collapsing symbol
/// </summary>
public class TextTrailingCharacterEllipsis : TextCollapsingProperties
{
private static readonly ReadOnlySlice<char> s_ellipsis = new ReadOnlySlice<char>(new[] { '\u2026' });
/// <summary>
/// Construct a text trailing character ellipsis collapsing properties
/// </summary>
/// <param name="width">width in which collapsing is constrained to</param>
/// <param name="textRunProperties">text run properties of ellispis symbol</param>
public TextTrailingCharacterEllipsis(double width, TextRunProperties textRunProperties)
{
Width = width;
Symbol = new TextCharacters(s_ellipsis, textRunProperties);
}
/// <inheritdoc/>
public sealed override double Width { get; }
/// <inheritdoc/>
public sealed override TextRun Symbol { get; }
/// <inheritdoc/>
public sealed override TextCollapsingStyle Style { get; } = TextCollapsingStyle.TrailingCharacter;
}
}

37
src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs

@ -0,0 +1,37 @@
using Avalonia.Utilities;
namespace Avalonia.Media.TextFormatting
{
/// <summary>
/// a collapsing properties to collapse whole line toward the end
/// at word granularity and with ellipsis being the collapsing symbol
/// </summary>
public class TextTrailingWordEllipsis : TextCollapsingProperties
{
private static readonly ReadOnlySlice<char> s_ellipsis = new ReadOnlySlice<char>(new[] { '\u2026' });
/// <summary>
/// Construct a text trailing word ellipsis collapsing properties
/// </summary>
/// <param name="width">width in which collapsing is constrained to</param>
/// <param name="textRunProperties">text run properties of ellispis symbol</param>
public TextTrailingWordEllipsis(
double width,
TextRunProperties textRunProperties
)
{
Width = width;
Symbol = new TextCharacters(s_ellipsis, textRunProperties);
}
/// <inheritdoc/>
public sealed override double Width { get; }
/// <inheritdoc/>
public sealed override TextRun Symbol { get; }
/// <inheritdoc/>
public sealed override TextCollapsingStyle Style { get; } = TextCollapsingStyle.TrailingWord;
}
}

2
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;
}

1
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

16
src/Avalonia.Visuals/Media/TextWrapping.cs

@ -5,13 +5,6 @@ namespace Avalonia.Media
/// </summary>
public enum TextWrapping
{
/// <summary>
/// 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.
/// </summary>
WrapWithOverflow,
/// <summary>
/// Text should not wrap.
/// </summary>
@ -20,6 +13,13 @@ namespace Avalonia.Media
/// <summary>
/// Text can wrap.
/// </summary>
Wrap
Wrap,
/// <summary>
/// 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.
/// </summary>
WrapWithOverflow
}
}

4
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));

47
src/Avalonia.X11/X11Window.cs

@ -163,7 +163,7 @@ namespace Avalonia.X11
var surfaces = new List<object>
{
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<object> Surfaces { get; }
public Action<RawInputEventArgs> 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);

4
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<object> Surfaces { get; }
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> 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) { }

14
src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>true</IsPackable>
</PropertyGroup>
<Import Project="IncludeXamlIlSre.props" />
<ItemGroup>
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
</ItemGroup>
</Project>

42
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
{
/// <summary>
/// Loads XAML from a string.
/// </summary>
/// <param name="xaml">The string containing the XAML.</param>
/// <param name="localAssembly">Default assembly for clr-namespace:</param>
/// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded.
/// </param>
/// <returns>The loaded object.</returns>
public static object Load(string xaml, Assembly localAssembly = null, object rootInstance = null, Uri uri = null, bool designMode = false)
{
Contract.Requires<ArgumentNullException>(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<T>(string xaml, Assembly localAssembly = null)
=> (T)Parse(xaml, localAssembly);
}
}

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs → src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AddNameScopeRegistration.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaBindingExtensionTransformer.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaBindingExtensionTransformer.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlCompiledBindingsMetadataRemover.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlCompiledBindingsMetadataRemover.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs

29
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs → 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<IXamlType> GetAllInterfacesIncludingSelf(IXamlType type)
{
if (type.IsInterface)
yield return type;
foreach (var i in type.GetAllInterfaces())
yield return i;
}
}
[DebuggerDisplay("DataType = {DataContextType}")]

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlDesignPropertiesTransformer.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDesignPropertiesTransformer.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlMetadataRemover.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlMetadataRemover.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlPropertyPathTransformer.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlPropertyPathTransformer.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlResolveByNameMarkupExtensionReplacer.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlResolveByNameMarkupExtensionReplacer.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlRootObjectScopeTransformer.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlRootObjectScopeTransformer.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransitionsTypeMetadataTransformer.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransitionsTypeMetadataTransformer.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/IgnoredDirectivesTransformer.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/IgnoredDirectivesTransformer.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/XNameTransformer.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/XNameTransformer.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlPropertyInfoAccessorFactoryEmitter.cs → src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlPropertyInfoAccessorFactoryEmitter.cs

12
src/Markup/Avalonia.Markup.Xaml.Loader/IncludeXamlIlSre.props

@ -0,0 +1,12 @@
<Project>
<ItemGroup>
<None Remove="$(MSBuildThisFileDirectory)\xamlil.github\**\*.*" />
<Content Remove="$(MSBuildThisFileDirectory)\xamlil.github\**\*.*" />
<Compile Remove="$(MSBuildThisFileDirectory)\xamlil.github\**\*.*" />
<Compile Include="$(MSBuildThisFileDirectory)\xamlil.github\src\XamlX\**\*.cs" />
<Compile Remove="$(MSBuildThisFileDirectory)\xamlil.github\**\obj\**\*.cs" />
<Compile Include="$(MSBuildThisFileDirectory)\..\Avalonia.Markup\Markup\Parsers\SelectorGrammar.cs" />
<Compile Include="$(MSBuildThisFileDirectory)\..\Avalonia.Markup.Xaml\Parsers\PropertyParser.cs" />
<Compile Include="$(MSBuildThisFileDirectory)\..\Avalonia.Markup\Markup\Parsers\BindingExpressionGrammar.cs" />
</ItemGroup>
</Project>

0
src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github → src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github

36
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -45,44 +45,10 @@
<Compile Include="Templates\Template.cs" />
<Compile Include="Templates\TemplateContent.cs" />
<Compile Include="Templates\TreeDataTemplate.cs" />
<Compile Include="XamlIl\AvaloniaXamlIlRuntimeCompiler.cs" />
<Compile Include="XamlIl\CompilerExtensions\AvaloniaXamlIlCompilerConfiguration.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaBindingExtensionTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlAvaloniaPropertyResolver.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlBindingPathParser.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlBindingPathTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlConstructorServiceProviderTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlDataContextTypeTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlDesignPropertiesTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlCompiledBindingsMetadataRemover.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlMetadataRemover.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlPropertyPathTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlResolveByNameMarkupExtensionReplacer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlRootObjectScopeTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlTransformInstanceAttachedProperties.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlTransitionsTypeMetadataTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlWellKnownTypes.cs" />
<Compile Include="XamlIl\CompilerExtensions\XamlIlAvaloniaPropertyHelper.cs" />
<Compile Include="XamlIl\CompilerExtensions\AvaloniaXamlIlCompiler.cs" />
<Compile Include="XamlIl\CompilerExtensions\AvaloniaXamlIlLanguage.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AddNameScopeRegistration.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlSetterTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\IgnoredDirectivesTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlSelectorTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\XNameTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\XamlIlBindingPathHelper.cs" />
<Compile Include="XamlIl\CompilerExtensions\XamlIlClrPropertyInfoHelper.cs" />
<Compile Include="XamlIl\CompilerExtensions\XamlIlPropertyInfoAccessorFactoryEmitter.cs" />
<Compile Include="XamlIl\Runtime\IAvaloniaXamlIlParentStackProvider.cs" />
<Compile Include="XamlIl\Runtime\IAvaloniaXamlIlXmlNamespaceInfoProviderV1.cs" />
<Compile Include="XamlIl\Runtime\XamlIlRuntimeHelpers.cs" />
<Compile Include="XamlLoadException.cs" />
<Compile Include="XamlIl\xamlil.github\src\XamlX\**\*.cs" />
<Compile Condition="$(UseCecil) == true" Include="XamlIl\xamlil.github\src\XamlX.Il.Cecil\**\*.cs" />
<Compile Remove="XamlIl\xamlil.github\**\obj\**\*.cs" />
<Compile Include="..\Avalonia.Markup\Markup\Parsers\SelectorGrammar.cs" />
<Compile Include="..\Avalonia.Markup\Markup\Parsers\BindingExpressionGrammar.cs" />
<Compile Include="XamlTypes.cs" />
</ItemGroup>
@ -96,8 +62,6 @@
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\Avalonia.Markup\Avalonia.Markup.csproj" />
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
<PackageReference Condition="$(UseCecil) == true" Include="Mono.Cecil" Version="0.10.3" />
</ItemGroup>
<Import Project="..\..\..\build\Rx.props" />
</Project>

67
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@ -10,10 +10,13 @@ namespace Avalonia.Markup.Xaml
/// <summary>
/// Loads XAML for a avalonia application.
/// </summary>
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);
}
/// <summary>
/// Loads the XAML into a Avalonia component.
/// </summary>
@ -32,7 +35,7 @@ namespace Avalonia.Markup.Xaml
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>The loaded object.</returns>
public object Load(Uri uri, Uri baseUri = null)
public static object Load(Uri uri, Uri baseUri = null)
{
Contract.Requires<ArgumentNullException>(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);
}
}
/// <summary>
/// Loads XAML from a string.
/// </summary>
/// <param name="xaml">The string containing the XAML.</param>
/// <param name="localAssembly">Default assembly for clr-namespace:</param>
/// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded.
/// </param>
/// <returns>The loaded object.</returns>
public object Load(string xaml, Assembly localAssembly = null, object rootInstance = null)
{
Contract.Requires<ArgumentNullException>(xaml != null);
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml)))
// This is intended for unit-tests only
var runtimeLoader = AvaloniaLocator.Current.GetService<IRuntimeXamlLoader>();
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);
}
}
}
/// <summary>
/// Loads XAML from a stream.
/// </summary>
/// <param name="stream">The stream containing the XAML.</param>
/// <param name="localAssembly">Default assembly for clr-namespace</param>
/// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded.
/// </param>
/// <param name="uri">The URI of the XAML</param>
/// <returns>The loaded object.</returns>
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<T>(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");
}
}
}

3
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;
}

3
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;
}

11
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;
}
}
}

2
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)

2
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)

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save