Browse Source

Merge branch 'master' into fixes/2191-tabcontrol-contentcontrolmixin

pull/2218/head
Steven Kirk 7 years ago
committed by GitHub
parent
commit
3b9702bfae
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .ncrunch/Avalonia.Desktop.v3.ncrunchproject
  2. 5
      .ncrunch/Avalonia.net461.v3.ncrunchproject
  3. 91
      Avalonia.sln
  4. 1
      Avalonia.v3.ncrunchsolution
  5. 19
      azure-pipelines.yml
  6. 5
      build/NetFX.props
  7. 8
      build/UnitTests.NetFX.props
  8. 1
      build/xunit.runner.mono.json
  9. 2
      global.json
  10. 58
      nukebuild/Build.cs
  11. 12
      nukebuild/BuildParameters.cs
  12. 1
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  13. 1
      samples/ControlCatalog/Pages/DialogsPage.xaml
  14. 19
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  15. 9
      samples/ControlCatalog/Pages/DropDownPage.xaml
  16. 3
      samples/ControlCatalog/Pages/DropDownPage.xaml.cs
  17. 4
      samples/ControlCatalog/Pages/ScreenPage.cs
  18. 6
      samples/PlatformSanityChecks/App.xaml
  19. 13
      samples/PlatformSanityChecks/App.xaml.cs
  20. 13
      samples/PlatformSanityChecks/PlatformSanityChecks.csproj
  21. 132
      samples/PlatformSanityChecks/Program.cs
  22. 6
      src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs
  23. 8
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  24. 21
      src/Avalonia.Animation/AnimationInstance`1.cs
  25. 1
      src/Avalonia.Animation/AnimatorKeyFrame.cs
  26. 2
      src/Avalonia.Animation/IterationCount.cs
  27. 4
      src/Avalonia.Base/Collections/AvaloniaDictionary.cs
  28. 2
      src/Avalonia.Base/Collections/AvaloniaList.cs
  29. 1
      src/Avalonia.Base/Platform/IAssetLoader.cs
  30. 2
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  31. 2
      src/Avalonia.Controls/Calendar/DatePicker.cs
  32. 4
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
  33. 4
      src/Avalonia.Controls/GridLength.cs
  34. 4
      src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
  35. 27
      src/Avalonia.Controls/PixelPointEventArgs.cs
  36. 4
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  37. 6
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  38. 8
      src/Avalonia.Controls/Platform/Screen.cs
  39. 27
      src/Avalonia.Controls/PointEventArgs.cs
  40. 2
      src/Avalonia.Controls/Primitives/AdornerLayer.cs
  41. 30
      src/Avalonia.Controls/Primitives/Popup.cs
  42. 10
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  43. 14
      src/Avalonia.Controls/Screens.cs
  44. 8
      src/Avalonia.Controls/TopLevel.cs
  45. 2
      src/Avalonia.Controls/Viewbox.cs
  46. 58
      src/Avalonia.Controls/Window.cs
  47. 10
      src/Avalonia.Controls/WindowBase.cs
  48. 4
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  49. 10
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  50. 6
      src/Avalonia.Desktop/AppBuilderDesktopExtensions.cs
  51. 4
      src/Avalonia.Desktop/Avalonia.Desktop.csproj
  52. 2
      src/Avalonia.Input/Cursors.cs
  53. 6
      src/Avalonia.Input/DataFormats.cs
  54. 10
      src/Avalonia.Input/DragDrop.cs
  55. 2
      src/Avalonia.Input/IMouseDevice.cs
  56. 6
      src/Avalonia.Input/MouseDevice.cs
  57. 2
      src/Avalonia.Input/Raw/RawDragEvent.cs
  58. 4
      src/Avalonia.Input/Raw/RawInputEventArgs.cs
  59. 2
      src/Avalonia.Input/Raw/RawKeyEventArgs.cs
  60. 2
      src/Avalonia.Input/Raw/RawMouseEventArgs.cs
  61. 2
      src/Avalonia.Input/Raw/RawMouseWheelEventArgs.cs
  62. 2
      src/Avalonia.Input/Raw/RawTextInputEventArgs.cs
  63. 15
      src/Avalonia.Native/Helpers.cs
  64. 5
      src/Avalonia.Native/ScreenImpl.cs
  65. 14
      src/Avalonia.Native/WindowImplBase.cs
  66. 6
      src/Avalonia.OpenGL/EglContext.cs
  67. 17
      src/Avalonia.OpenGL/EglDisplay.cs
  68. 21
      src/Avalonia.OpenGL/EglErrors.cs
  69. 7
      src/Avalonia.OpenGL/EglInterface.cs
  70. 3
      src/Avalonia.OpenGL/GlConsts.cs
  71. 15
      src/Avalonia.OpenGL/GlErrors.cs
  72. 5
      src/Avalonia.OpenGL/GlInterface.cs
  73. 30
      src/Avalonia.OpenGL/OpenGlException.cs
  74. 20
      src/Avalonia.Styling/Styling/ChildSelector.cs
  75. 16
      src/Avalonia.Styling/Styling/DescendentSelector.cs
  76. 7
      src/Avalonia.Styling/Styling/IStyle.cs
  77. 6
      src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs
  78. 39
      src/Avalonia.Styling/Styling/Selector.cs
  79. 86
      src/Avalonia.Styling/Styling/SelectorMatch.cs
  80. 2
      src/Avalonia.Styling/Styling/Setter.cs
  81. 24
      src/Avalonia.Styling/Styling/Style.cs
  82. 45
      src/Avalonia.Styling/Styling/Styles.cs
  83. 5
      src/Avalonia.Styling/Styling/TemplateSelector.cs
  84. 19
      src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs
  85. 7
      src/Avalonia.Visuals/Media/FontFamily.cs
  86. 4
      src/Avalonia.Visuals/Media/GradientStop.cs
  87. 2
      src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
  88. 201
      src/Avalonia.Visuals/Media/PixelPoint.cs
  89. 436
      src/Avalonia.Visuals/Media/PixelRect.cs
  90. 50
      src/Avalonia.Visuals/Media/PixelSize.cs
  91. 2
      src/Avalonia.Visuals/Media/Typeface.cs
  92. 5
      src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs
  93. 56
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  94. 8
      src/Avalonia.Visuals/Rendering/IRenderRoot.cs
  95. 12
      src/Avalonia.Visuals/Rendering/RenderLayer.cs
  96. 6
      src/Avalonia.Visuals/Rendering/RenderLayers.cs
  97. 10
      src/Avalonia.Visuals/VisualExtensions.cs
  98. 14
      src/Avalonia.X11/Avalonia.X11.csproj
  99. 2108
      src/Avalonia.X11/Keysyms.cs
  100. 17
      src/Avalonia.X11/Stubs.cs

5
.ncrunch/Avalonia.Desktop.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

5
.ncrunch/Avalonia.net461.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

91
Avalonia.sln

@ -1,4 +1,5 @@
Microsoft Visual Studio Solution File, Format Version 12.00

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2027
MinimumVisualStudioVersion = 10.0.40219.1
@ -130,6 +131,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
ProjectSection(SolutionItems) = preProject
build\Base.props = build\Base.props
build\Binding.props = build\Binding.props
build\BuildTargets.targets = build\BuildTargets.targets
build\JetBrains.Annotations.props = build\JetBrains.Annotations.props
build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props
build\Magick.NET-Q16-AnyCPU.props = build\Magick.NET-Q16-AnyCPU.props
@ -147,7 +149,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\Splat.props = build\Splat.props
build\System.Memory.props = build\System.Memory.props
build\XUnit.props = build\XUnit.props
build\BuildTargets.targets = build\BuildTargets.targets
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}"
@ -189,11 +190,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia", "packages\Avalon
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Desktop", "src\Avalonia.Desktop\Avalonia.Desktop.csproj", "{3C471044-3640-45E3-B1B2-16D2FF8399EE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Build.Tasks", "src\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj", "{BF28998D-072C-439A-AFBB-2FE5021241E0}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Build.Tasks", "src\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj", "{BF28998D-072C-439A-AFBB-2FE5021241E0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "nukebuild\_build.csproj", "{3F00BC43-5095-477F-93D8-E65B08179A00}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Animation.UnitTests", "tests\Avalonia.Animation.UnitTests\Avalonia.Animation.UnitTests.csproj", "{AF227847-E65C-4BE9-BCE9-B551357788E0}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Animation.UnitTests", "tests\Avalonia.Animation.UnitTests\Avalonia.Animation.UnitTests.csproj", "{AF227847-E65C-4BE9-BCE9-B551357788E0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.X11", "src\Avalonia.X11\Avalonia.X11.csproj", "{41B02319-965D-4945-8005-C1A3D1224165}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlatformSanityChecks", "samples\PlatformSanityChecks\PlatformSanityChecks.csproj", "{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
@ -220,10 +225,6 @@ Global
Release|iPhoneSimulator = Release|iPhoneSimulator
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.Build.0 = Release|Any CPU
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
@ -1722,6 +1723,30 @@ Global
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhone.Build.0 = Release|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|Any CPU.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhone.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhone.Build.0 = Debug|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhone.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhone.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
@ -1746,6 +1771,54 @@ Global
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhone.Build.0 = Release|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.AppStore|iPhone.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Debug|Any CPU.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Debug|iPhone.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Release|Any CPU.ActiveCfg = Release|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Release|Any CPU.Build.0 = Release|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Release|iPhone.ActiveCfg = Release|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Release|iPhone.Build.0 = Release|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|iPhone.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|iPhone.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|Any CPU.Build.0 = Release|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|iPhone.ActiveCfg = Release|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|iPhone.Build.0 = Release|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1800,6 +1873,8 @@ Global
{D49233F8-F29C-47DD-9975-C4C9E4502720} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C}
{3C471044-3640-45E3-B1B2-16D2FF8399EE} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C}
{AF227847-E65C-4BE9-BCE9-B551357788E0} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{41B02319-965D-4945-8005-C1A3D1224165} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

1
Avalonia.v3.ncrunchsolution

@ -2,6 +2,7 @@
<Settings>
<AdditionalFilesToIncludeForSolution>
<Value>tests\TestFiles\**.*</Value>
<Value>src\Avalonia.Build.Tasks\bin\Debug\netstandard2.0\Avalonia.Build.Tasks.dll</Value>
</AdditionalFilesToIncludeForSolution>
<AllowParallelTestExecution>True</AllowParallelTestExecution>
<ProjectConfigStoragePathRelativeToSolutionDir>.ncrunch</ProjectConfigStoragePathRelativeToSolutionDir>

19
azure-pipelines.yml

@ -38,6 +38,13 @@ jobs:
inputs:
version: '2.1.403'
- task: CmdLine@2
displayName: 'Install Mono 5.18'
inputs:
script: |
curl -o ./mono.pkg https://download.mono-project.com/archive/5.18.0/macos-10-universal/MonoFramework-MDK-5.18.0.225.macos10.xamarin.universal.pkg
sudo installer -verbose -pkg ./mono.pkg -target /
- task: Xcode@5
inputs:
actions: 'build'
@ -51,7 +58,9 @@ jobs:
- task: CmdLine@2
displayName: 'Install CastXML'
inputs:
script: brew install castxml
script: |
brew update
brew install castxml
- task: CmdLine@2
displayName: 'Install Nuke'
@ -83,13 +92,13 @@ jobs:
inputs:
pathToPublish: '$(Build.SourcesDirectory)/Build/Products/Release/'
artifactName: 'Avalonia.Native.OSX'
condition: and(succeeded(), eq(variables['system.pullrequest.isfork'], false))
condition: succeeded()
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: '$(Build.SourcesDirectory)/artifacts/nuget'
artifactName: 'NuGetOSX'
condition: and(succeeded(), eq(variables['system.pullrequest.isfork'], false))
condition: succeeded()
- job: Windows
pool:
@ -118,10 +127,10 @@ jobs:
inputs:
pathtoPublish: '$(Build.SourcesDirectory)/artifacts/nuget'
artifactName: 'NuGet'
condition: and(succeeded(), eq(variables['system.pullrequest.isfork'], false))
condition: succeeded()
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: '$(Build.SourcesDirectory)/artifacts/zip'
artifactName: 'Samples'
condition: and(succeeded(), eq(variables['system.pullrequest.isfork'], false))
condition: succeeded()

5
build/NetFX.props

@ -3,4 +3,9 @@
<FrameworkPathOverride>/usr/lib/mono/4.6.1-api</FrameworkPathOverride>
<FrameworkPathOverride Condition="$([MSBuild]::IsOsPlatform('OSX'))">/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.6.1-api</FrameworkPathOverride>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net47' and '$(OS)' == 'Unix' ">
<FrameworkPathOverride>/usr/lib/mono/4.7-api/</FrameworkPathOverride>
<FrameworkPathOverride Condition="$([MSBuild]::IsOsPlatform('OSX'))">/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.7-api</FrameworkPathOverride>
</PropertyGroup>
</Project>

8
build/UnitTests.NetFX.props

@ -0,0 +1,8 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildThisFileDirectory)/NetFX.props" />
<ItemGroup Condition="$(TargetFramework.StartsWith('net4'))">
<Content Include="$(MSBuildThisFileDirectory)/xunit.runner.mono.json" Link="xunit.runner.json" >
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

1
build/xunit.runner.mono.json

@ -0,0 +1 @@
{ "appDomain": "denied" }

2
global.json

@ -1,7 +1,7 @@
{
"msbuild-sdks": {
"Microsoft.Build.Traversal": "1.0.43",
"MSBuild.Sdk.Extras": "1.6.46",
"MSBuild.Sdk.Extras": "1.6.65",
"AggregatePackage.NuGet.Sdk" : "0.1.12"
}
}

58
nukebuild/Build.cs

@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Xml.Linq;
using Nuke.Common;
using Nuke.Common.Git;
using Nuke.Common.ProjectModel;
@ -56,6 +57,17 @@ partial class Build : NukeBuild
Information("IsReleasable: " + Parameters.IsReleasable);
Information("IsMyGetRelease: " + Parameters.IsMyGetRelease);
Information("IsNuGetRelease: " + Parameters.IsNuGetRelease);
void ExecWait(string preamble, string command, string args)
{
Console.WriteLine(preamble);
Process.Start(new ProcessStartInfo(command, args) {UseShellExecute = false}).WaitForExit();
}
ExecWait("dotnet version:", "dotnet", "--version");
if (Parameters.IsRunningOnUnix)
ExecWait("Mono version:", "mono", "--version");
}
Target Clean => _ => _.Executes(() =>
@ -92,16 +104,24 @@ partial class Build : NukeBuild
);
});
void RunCoreTest(string project, bool coreOnly = false)
void RunCoreTest(string project)
{
if(!project.EndsWith(".csproj"))
project = System.IO.Path.Combine(project, System.IO.Path.GetFileName(project)+".csproj");
Information("Running tests from " + project);
var frameworks = new List<string>(){"netcoreapp2.0"};
XDocument xdoc;
using (var s = File.OpenRead(project))
xdoc = XDocument.Load(s);
List<string> frameworks = null;
var targets = xdoc.Root.Descendants("TargetFrameworks").FirstOrDefault();
if (targets != null)
frameworks = targets.Value.Split(';').Where(f => !string.IsNullOrWhiteSpace(f)).ToList();
else
frameworks = new List<string> {xdoc.Root.Descendants("TargetFramework").First().Value};
foreach(var fw in frameworks)
{
if(!fw.StartsWith("netcoreapp") && coreOnly)
continue;
Information("Running for " + fw);
DotNetTest(c =>
{
@ -124,18 +144,18 @@ partial class Build : NukeBuild
.DependsOn(Compile)
.Executes(() =>
{
RunCoreTest("./tests/Avalonia.Animation.UnitTests", false);
RunCoreTest("./tests/Avalonia.Base.UnitTests", false);
RunCoreTest("./tests/Avalonia.Controls.UnitTests", false);
RunCoreTest("./tests/Avalonia.Input.UnitTests", false);
RunCoreTest("./tests/Avalonia.Interactivity.UnitTests", false);
RunCoreTest("./tests/Avalonia.Layout.UnitTests", false);
RunCoreTest("./tests/Avalonia.Markup.UnitTests", false);
RunCoreTest("./tests/Avalonia.Markup.Xaml.UnitTests", false);
RunCoreTest("./tests/Avalonia.Styling.UnitTests", false);
RunCoreTest("./tests/Avalonia.Visuals.UnitTests", false);
RunCoreTest("./tests/Avalonia.Skia.UnitTests", false);
RunCoreTest("./tests/Avalonia.ReactiveUI.UnitTests", false);
RunCoreTest("./tests/Avalonia.Animation.UnitTests");
RunCoreTest("./tests/Avalonia.Base.UnitTests");
RunCoreTest("./tests/Avalonia.Controls.UnitTests");
RunCoreTest("./tests/Avalonia.Input.UnitTests");
RunCoreTest("./tests/Avalonia.Interactivity.UnitTests");
RunCoreTest("./tests/Avalonia.Layout.UnitTests");
RunCoreTest("./tests/Avalonia.Markup.UnitTests");
RunCoreTest("./tests/Avalonia.Markup.Xaml.UnitTests");
RunCoreTest("./tests/Avalonia.Styling.UnitTests");
RunCoreTest("./tests/Avalonia.Visuals.UnitTests");
RunCoreTest("./tests/Avalonia.Skia.UnitTests");
RunCoreTest("./tests/Avalonia.ReactiveUI.UnitTests");
});
Target RunRenderTests => _ => _
@ -143,9 +163,9 @@ partial class Build : NukeBuild
.DependsOn(Compile)
.Executes(() =>
{
RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", true);
RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", true);
RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj");
});
Target RunDesignerTests => _ => _
@ -153,7 +173,7 @@ partial class Build : NukeBuild
.DependsOn(Compile)
.Executes(() =>
{
RunCoreTest("./tests/Avalonia.DesignerSupport.Tests", false);
RunCoreTest("./tests/Avalonia.DesignerSupport.Tests");
});
[PackageExecutable("JetBrains.dotMemoryUnit", "dotMemoryUnit.exe")] readonly Tool DotMemoryUnit;

12
nukebuild/BuildParameters.cs

@ -14,13 +14,13 @@ using static Nuke.Common.Tools.MSBuild.MSBuildTasks;
public partial class Build
{
[Parameter("configuration")]
public string NukeArgConfiguration { get; set; }
public string Configuration { get; set; }
[Parameter("skip-tests")]
public bool NukeArgSkipTests { get; set; }
public bool SkipTests { get; set; }
[Parameter("force-nuget-version")]
public string NukeArgForceNugetVersion { get; set; }
public string ForceNugetVersion { get; set; }
public class BuildParameters
{
@ -64,8 +64,8 @@ public partial class Build
public BuildParameters(Build b)
{
// ARGUMENTS
Configuration = b.NukeArgConfiguration ?? "Release";
SkipTests = b.NukeArgSkipTests;
Configuration = b.Configuration ?? "Release";
SkipTests = b.SkipTests;
// CONFIGURATION
MainRepo = "https://github.com/AvaloniaUI/Avalonia";
@ -102,7 +102,7 @@ public partial class Build
IsNuGetRelease = IsMainRepo && IsReleasable && IsReleaseBranch;
// VERSION
Version = b.NukeArgForceNugetVersion ?? GetVersion();
Version = b.ForceNugetVersion ?? GetVersion();
if (IsRunningOnAzure)
{

1
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

@ -9,6 +9,7 @@
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Desktop\Avalonia.Desktop.csproj" />
<ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
</ItemGroup>

1
samples/ControlCatalog/Pages/DialogsPage.xaml

@ -6,6 +6,7 @@
<Button Name="SaveFile">Save File</Button>
<Button Name="SelectFolder">Select Folder</Button>
<Button Name="DecoratedWindow">Decorated window</Button>
<Button Name="DecoratedWindowDialog">Decorated window (dialog)</Button>
<Button Name="Dialog">Dialog</Button>
</StackPanel>
</UserControl>

19
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@ -31,15 +31,22 @@ namespace ControlCatalog.Pages
}.ShowAsync(GetWindow());
};
this.FindControl<Button>("DecoratedWindow").Click += delegate
{
new DecoratedWindow().ShowDialog(GetWindow());
};
{
new DecoratedWindow().Show();
};
this.FindControl<Button>("DecoratedWindowDialog").Click += delegate
{
new DecoratedWindow().ShowDialog(GetWindow());
};
this.FindControl<Button>("Dialog").Click += delegate
{
new MainWindow().ShowDialog(GetWindow());
var window = new Window();
window.Height = 200;
window.Width = 200;
window.Content = new TextBlock { Text = "Hello world!" };
window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
window.ShowDialog(GetWindow());
};
}
Window GetWindow() => (Window)this.VisualRoot;

9
samples/ControlCatalog/Pages/DropDownPage.xaml

@ -27,6 +27,15 @@
<TextBox Text="TextBox"/>
</DropDownItem>
</DropDown>
<DropDown x:Name="fontDropDown" SelectedIndex="0">
<DropDown.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontFamily="{Binding}" />
</DataTemplate>
</DropDown.ItemTemplate>
</DropDown>
</StackPanel>
</StackPanel>

3
samples/ControlCatalog/Pages/DropDownPage.xaml.cs

@ -13,6 +13,9 @@ namespace ControlCatalog.Pages
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
var fontDropDown = this.Find<DropDown>("fontDropDown");
fontDropDown.Items = Avalonia.Media.FontFamily.SystemFontFamilies;
fontDropDown.SelectedIndex = 0;
}
}
}

4
samples/ControlCatalog/Pages/ScreenPage.cs

@ -4,6 +4,7 @@ using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering;
namespace ControlCatalog.Pages
{
@ -23,6 +24,7 @@ namespace ControlCatalog.Pages
base.Render(context);
Window w = (Window)VisualRoot;
Screen[] screens = w.Screens.All;
var scaling = ((IRenderRoot)w).RenderScaling;
Pen p = new Pen(Brushes.Black);
if (screens != null)
@ -56,7 +58,7 @@ namespace ControlCatalog.Pages
text.Text = $"Primary: {screen.Primary}";
context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 40), text);
text.Text = $"Current: {screen.Equals(w.Screens.ScreenFromBounds(new Rect(w.Position, w.Bounds.Size)))}";
text.Text = $"Current: {screen.Equals(w.Screens.ScreenFromBounds(new PixelRect(w.Position, PixelSize.FromSize(w.Bounds.Size, scaling))))}";
context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 60), text);
}

6
samples/PlatformSanityChecks/App.xaml

@ -0,0 +1,6 @@
<Application xmlns="https://github.com/avaloniaui">
<Application.Styles>
<StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/>
</Application.Styles>
</Application>

13
samples/PlatformSanityChecks/App.xaml.cs

@ -0,0 +1,13 @@
using Avalonia;
using Avalonia.Markup.Xaml;
namespace PlatformSanityChecks
{
public class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
}
}

13
samples/PlatformSanityChecks/PlatformSanityChecks.csproj

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Desktop\Avalonia.Desktop.csproj" />
<ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
</ItemGroup>
</Project>

132
samples/PlatformSanityChecks/Program.cs

@ -0,0 +1,132 @@
using System;
using System.Diagnostics;
using System.Reactive.Disposables;
using System.Runtime.CompilerServices;
using System.Threading;
using Avalonia;
using Avalonia.Platform;
using Avalonia.Threading;
using Avalonia.X11;
namespace PlatformSanityChecks
{
public class Program
{
static Thread UiThread;
static void Main(string[] args)
{
UiThread = Thread.CurrentThread;
AppBuilder.Configure<App>().RuntimePlatformServicesInitializer();
var app = new App();
AvaloniaX11PlatformExtensions.InitializeX11Platform();
CheckPlatformThreading();
}
static bool CheckAccess() => UiThread == Thread.CurrentThread;
static void VerifyAccess()
{
if (!CheckAccess())
Die("Call from invalid thread");
}
static Exception Die(string error)
{
Console.Error.WriteLine(error);
Console.Error.WriteLine(Environment.StackTrace);
Process.GetCurrentProcess().Kill();
throw new Exception(error);
}
static IDisposable Enter([CallerMemberName] string caller = null)
{
Console.WriteLine("Entering " + caller);
return Disposable.Create(() => { Console.WriteLine("Leaving " + caller); });
}
static void EnterLoop(Action<CancellationTokenSource> cb, [CallerMemberName] string caller = null)
{
using (Enter(caller))
{
var cts = new CancellationTokenSource();
cb(cts);
Dispatcher.UIThread.MainLoop(cts.Token);
if (!cts.IsCancellationRequested)
Die("Unexpected loop exit");
}
}
static void CheckTimerOrdering() => EnterLoop(cts =>
{
bool firstFired = false, secondFired = false;
DispatcherTimer.Run(() =>
{
Console.WriteLine("Second tick");
VerifyAccess();
if (!firstFired)
throw Die("Invalid timer ordering");
if (secondFired)
throw Die("Invocation of finished timer");
secondFired = true;
cts.Cancel();
return false;
}, TimeSpan.FromSeconds(2));
DispatcherTimer.Run(() =>
{
Console.WriteLine("First tick");
VerifyAccess();
if (secondFired)
throw Die("Invalid timer ordering");
if (firstFired)
throw Die("Invocation of finished timer");
firstFired = true;
return false;
}, TimeSpan.FromSeconds(1));
});
static void CheckTimerTicking() => EnterLoop(cts =>
{
int ticks = 0;
var st = Stopwatch.StartNew();
DispatcherTimer.Run(() =>
{
ticks++;
Console.WriteLine($"Tick {ticks} at {st.Elapsed}");
if (ticks == 5)
{
if (st.Elapsed.TotalSeconds < 4.5)
Die("Timer is too fast");
if (st.Elapsed.TotalSeconds > 6)
Die("Timer is too slow");
cts.Cancel();
return false;
}
return true;
}, TimeSpan.FromSeconds(1));
});
static void CheckSignaling() => EnterLoop(cts =>
{
ThreadPool.QueueUserWorkItem(_ =>
{
Thread.Sleep(100);
Dispatcher.UIThread.Post(() =>
{
VerifyAccess();
cts.Cancel();
});
});
});
static void CheckPlatformThreading()
{
CheckSignaling();
CheckTimerOrdering();
CheckTimerTicking();
}
}
}

6
src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs

@ -10,10 +10,10 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{
class PopupImpl : TopLevelImpl, IPopupImpl
{
private Point _position;
private PixelPoint _position;
private bool _isAdded;
Action IWindowBaseImpl.Activated { get; set; }
public Action<Point> PositionChanged { get; set; }
public Action<PixelPoint> PositionChanged { get; set; }
public Action Deactivated { get; set; }
public PopupImpl() : base(ActivityTracker.Current, true)
@ -36,7 +36,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public IScreenImpl Screen { get; }
public Point Position
public PixelPoint Position
{
get { return _position; }
set

8
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -99,14 +99,14 @@ namespace Avalonia.Android.Platform.SkiaPlatform
if (_view.Holder?.Surface?.IsValid == true) _view.Invalidate();
}
public Point PointToClient(Point point)
public Point PointToClient(PixelPoint point)
{
return point;
return point.ToPoint(1);
}
public Point PointToScreen(Point point)
public PixelPoint PointToScreen(Point point)
{
return point;
return PixelPoint.FromPoint(point, 1);
}
public void SetCursor(IPlatformHandle cursor)

21
src/Avalonia.Animation/AnimationInstance`1.cs

@ -19,6 +19,7 @@ namespace Avalonia.Animation
private ulong? _iterationCount;
private ulong _currentIteration;
private bool _gotFirstKFValue;
private bool _playbackReversed;
private FillMode _fillMode;
private PlaybackDirection _playbackDirection;
private Animator<T> _animator;
@ -160,9 +161,14 @@ namespace Avalonia.Animation
_currentIteration = (ulong)(opsTime / iterationTime);
// Stop animation when the current iteration is beyond the iteration count.
// Stop animation when the current iteration is beyond the iteration count
// and snap the last iteration value to exact values.
if ((_currentIteration + 1) > _iterationCount)
{
var easedTime = _easeFunc.Ease(_playbackReversed ? 0.0 : 1.0);
_lastInterpValue = _interpolator(easedTime, _neutralValue);
DoComplete();
}
if (playbackTime <= iterDuration)
{
@ -170,27 +176,26 @@ namespace Avalonia.Animation
var normalizedTime = playbackTime / iterDuration;
// Check if normalized time needs to be reversed according to PlaybackDirection
bool playbackReversed;
switch (_playbackDirection)
{
case PlaybackDirection.Normal:
playbackReversed = false;
_playbackReversed = false;
break;
case PlaybackDirection.Reverse:
playbackReversed = true;
_playbackReversed = true;
break;
case PlaybackDirection.Alternate:
playbackReversed = (_currentIteration % 2 == 0) ? false : true;
_playbackReversed = (_currentIteration % 2 == 0) ? false : true;
break;
case PlaybackDirection.AlternateReverse:
playbackReversed = (_currentIteration % 2 == 0) ? true : false;
_playbackReversed = (_currentIteration % 2 == 0) ? true : false;
break;
default:
throw new InvalidOperationException($"Animation direction value is unknown: {_playbackDirection}");
}
if (playbackReversed)
if (_playbackReversed)
normalizedTime = 1 - normalizedTime;
// Ease and interpolate

1
src/Avalonia.Animation/AnimatorKeyFrame.cs

@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
using Avalonia.Animation.Animators;
using Avalonia.Data;
using Avalonia.Reactive;

2
src/Avalonia.Animation/IterationCount.cs

@ -44,7 +44,7 @@ namespace Avalonia.Animation
{
if (type > IterationType.Infinite)
{
throw new ArgumentException("Invalid value", "type");
throw new ArgumentException("Invalid value", nameof(type));
}
_type = type;

4
src/Avalonia.Base/Collections/AvaloniaDictionary.cs

@ -148,7 +148,7 @@ namespace Avalonia.Collections
{
if (_inner.TryGetValue(key, out TValue value))
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
if (CollectionChanged != null)
@ -209,7 +209,7 @@ namespace Avalonia.Collections
private void NotifyAdd(TKey key, TValue value)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));

2
src/Avalonia.Base/Collections/AvaloniaList.cs

@ -511,7 +511,7 @@ namespace Avalonia.Collections
/// </summary>
private void NotifyCountChanged()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
}
/// <summary>

1
src/Avalonia.Base/Platform/IAssetLoader.cs

@ -65,6 +65,7 @@ namespace Avalonia.Platform
/// Gets all assets of a folder and subfolders that match specified uri.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">The base URI.</param>
/// <returns>All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset</returns>
IEnumerable<Uri> GetAssets(Uri uri, Uri baseUri);
}

2
src/Avalonia.Controls/Calendar/CalendarItem.cs

@ -52,7 +52,7 @@ namespace Avalonia.Controls.Primitives
internal Calendar Owner { get; set; }
internal CalendarDayButton CurrentButton { get; set; }
public static StyledProperty<IBrush> HeaderBackgroundProperty = Calendar.HeaderBackgroundProperty.AddOwner<CalendarItem>();
public static readonly StyledProperty<IBrush> HeaderBackgroundProperty = Calendar.HeaderBackgroundProperty.AddOwner<CalendarItem>();
public IBrush HeaderBackground
{
get { return GetValue(HeaderBackgroundProperty); }

2
src/Avalonia.Controls/Calendar/DatePicker.cs

@ -954,7 +954,7 @@ namespace Avalonia.Controls
}
else
{
var dateValidationError = new DatePickerDateValidationErrorEventArgs(new ArgumentOutOfRangeException("text", "SelectedDate value is not valid."), text);
var dateValidationError = new DatePickerDateValidationErrorEventArgs(new ArgumentOutOfRangeException(nameof(text), "SelectedDate value is not valid."), text);
OnDateValidationError(dateValidationError);
if (dateValidationError.ThrowException)

4
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs

@ -49,9 +49,9 @@ namespace Avalonia.Controls.Embedding.Offscreen
public Action<double> ScalingChanged { get; set; }
public void SetInputRoot(IInputRoot inputRoot) => InputRoot = inputRoot;
public virtual Point PointToClient(Point point) => point;
public virtual Point PointToClient(PixelPoint point) => point.ToPoint(1);
public virtual Point PointToScreen(Point point) => point;
public virtual PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, 1);
public virtual void SetCursor(IPlatformHandle cursor)
{

4
src/Avalonia.Controls/GridLength.cs

@ -56,12 +56,12 @@ namespace Avalonia.Controls
{
if (value < 0 || double.IsNaN(value) || double.IsInfinity(value))
{
throw new ArgumentException("Invalid value", "value");
throw new ArgumentException("Invalid value", nameof(value));
}
if (type < GridUnitType.Auto || type > GridUnitType.Star)
{
throw new ArgumentException("Invalid value", "type");
throw new ArgumentException("Invalid value", nameof(type));
}
_type = type;

4
src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs

@ -487,7 +487,7 @@ namespace Avalonia.Controls
{
if (e == null)
{
throw new ArgumentNullException("e");
throw new ArgumentNullException(nameof(e));
}
var handler = Spinned;
@ -812,7 +812,7 @@ namespace Avalonia.Controls
/// <summary>
/// Raised when the <see cref="Value"/> changes.
/// </summary>
public event EventHandler<SpinEventArgs> ValueChanged
public event EventHandler<NumericUpDownValueChangedEventArgs> ValueChanged
{
add { AddHandler(ValueChangedEvent, value); }
remove { RemoveHandler(ValueChangedEvent, value); }

27
src/Avalonia.Controls/PixelPointEventArgs.cs

@ -0,0 +1,27 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Controls
{
/// <summary>
/// Provides <see cref="PixelPoint"/> data for events.
/// </summary>
public class PixelPointEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="PixelPointEventArgs"/> class.
/// </summary>
/// <param name="point">The <see cref=PixelPoint"/> data.</param>
public PixelPointEventArgs(PixelPoint point)
{
Point = point;
}
/// <summary>
/// Gets the <see cref="PixelPoint"/> data.
/// </summary>
public PixelPoint Point { get; }
}
}

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

@ -82,14 +82,14 @@ namespace Avalonia.Platform
/// </summary>
/// <param name="point">The point in screen coordinates.</param>
/// <returns>The point in client coordinates.</returns>
Point PointToClient(Point point);
Point PointToClient(PixelPoint point);
/// <summary>
/// Converts a point from client to screen coordinates.
/// </summary>
/// <param name="point">The point in client coordinates.</param>
/// <returns>The point in screen coordinates.</returns>
Point PointToScreen(Point point);
PixelPoint PointToScreen(Point point);
/// <summary>
/// Sets the cursor associated with the toplevel.

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

@ -27,14 +27,14 @@ namespace Avalonia.Platform
void BeginResizeDrag(WindowEdge edge);
/// <summary>
/// Gets position of the window relatively to the screen
/// Gets the position of the window in device pixels.
/// </summary>
Point Position { get; set; }
PixelPoint Position { get; set; }
/// <summary>
/// Gets or sets a method called when the window's position changes.
/// </summary>
Action<Point> PositionChanged { get; set; }
Action<PixelPoint> PositionChanged { get; set; }
/// <summary>
/// Activates the window.

8
src/Avalonia.Controls/Platform/Screen.cs

@ -2,17 +2,17 @@
{
public class Screen
{
public Rect Bounds { get; }
public PixelRect Bounds { get; }
public Rect WorkingArea { get; }
public PixelRect WorkingArea { get; }
public bool Primary { get; }
public Screen(Rect bounds, Rect workingArea, bool primary)
public Screen(PixelRect bounds, PixelRect workingArea, bool primary)
{
this.Bounds = bounds;
this.WorkingArea = workingArea;
this.Primary = primary;
}
}
}
}

27
src/Avalonia.Controls/PointEventArgs.cs

@ -1,27 +0,0 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Controls
{
/// <summary>
/// Provides <see cref="Point"/> data for events.
/// </summary>
public class PointEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="PointEventArgs"/> class.
/// </summary>
/// <param name="point">The <see cref="Point"/> data.</param>
public PointEventArgs(Point point)
{
Point = point;
}
/// <summary>
/// Gets the <see cref="Point"/> data.
/// </summary>
public Point Point { get; }
}
}

2
src/Avalonia.Controls/Primitives/AdornerLayer.cs

@ -13,7 +13,7 @@ namespace Avalonia.Controls.Primitives
// TODO: Need to track position of adorned elements and move the adorner if they move.
public class AdornerLayer : Panel, ICustomSimpleHitTest
{
public static AttachedProperty<Visual> AdornedElementProperty =
public static readonly AttachedProperty<Visual> AdornedElementProperty =
AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, Visual>("AdornedElement");
private static readonly AttachedProperty<AdornedElementInfo> s_adornedElementInfoProperty =

30
src/Avalonia.Controls/Primitives/Popup.cs

@ -380,7 +380,7 @@ namespace Avalonia.Controls.Primitives
/// Gets the position for the popup based on the placement properties.
/// </summary>
/// <returns>The popup's position in screen coordinates.</returns>
protected virtual Point GetPosition()
protected virtual PixelPoint GetPosition()
{
var result = GetPosition(PlacementTarget ?? this.GetVisualParent<Control>(), PlacementMode, PopupRoot,
HorizontalOffset, VerticalOffset);
@ -388,35 +388,31 @@ namespace Avalonia.Controls.Primitives
return result;
}
internal static Point GetPosition(Control target, PlacementMode placement, PopupRoot popupRoot, double horizontalOffset, double verticalOffset)
internal static PixelPoint GetPosition(Control target, PlacementMode placement, PopupRoot popupRoot, double horizontalOffset, double verticalOffset)
{
var zero = default(Point);
var mode = placement;
if (target?.GetVisualRoot() == null)
{
mode = PlacementMode.Pointer;
}
var root = target?.GetVisualRoot();
var mode = root != null ? placement : PlacementMode.Pointer;
var scaling = root?.RenderScaling ?? 1;
switch (mode)
{
case PlacementMode.Pointer:
if (popupRoot != null)
{
// Scales the Horizontal and Vertical offset to screen co-ordinates.
var screenOffset = new Point(horizontalOffset * (popupRoot as ILayoutRoot).LayoutScaling,
verticalOffset * (popupRoot as ILayoutRoot).LayoutScaling);
return (((IInputRoot)popupRoot)?.MouseDevice?.Position ?? default(Point)) + screenOffset;
var screenOffset = PixelPoint.FromPoint(new Point(horizontalOffset, verticalOffset), scaling);
var mouseOffset = ((IInputRoot)popupRoot)?.MouseDevice?.Position ?? default;
return new PixelPoint(
screenOffset.X + mouseOffset.X,
screenOffset.Y + mouseOffset.Y);
}
return default(Point);
return default;
case PlacementMode.Bottom:
return target?.PointToScreen(new Point(0 + horizontalOffset, target.Bounds.Height + verticalOffset)) ??
zero;
return target?.PointToScreen(new Point(0 + horizontalOffset, target.Bounds.Height + verticalOffset)) ?? default;
case PlacementMode.Right:
return target?.PointToScreen(new Point(target.Bounds.Width + horizontalOffset, 0 + verticalOffset)) ?? zero;
return target?.PointToScreen(new Point(target.Bounds.Width + horizontalOffset, 0 + verticalOffset)) ?? default;
default:
throw new InvalidOperationException("Invalid value for Popup.PlacementMode");

10
src/Avalonia.Controls/Primitives/PopupRoot.cs

@ -85,18 +85,18 @@ namespace Avalonia.Controls.Primitives
if (screen != null)
{
var scaling = VisualRoot.RenderScaling;
var screenX = Position.X + (Bounds.Width * scaling) - screen.Bounds.X;
var screenY = Position.Y + (Bounds.Height * scaling) - screen.Bounds.Y;
var bounds = PixelRect.FromRect(Bounds, scaling);
var screenX = Position.X + bounds.Width - screen.Bounds.X;
var screenY = Position.Y + bounds.Height - screen.Bounds.Y;
if (screenX > screen.Bounds.Width)
{
Position = Position.WithX(Position.X - (screenX - screen.Bounds.Width));
Position = Position.WithX(Position.X - screenX - bounds.Width);
}
if (screenY > screen.Bounds.Height)
{
Position = Position.WithY(Position.Y - (screenY - screen.Bounds.Height));
Position = Position.WithY(Position.Y - screenY - bounds.Height);
}
}
}

14
src/Avalonia.Controls/Screens.cs

@ -18,7 +18,7 @@ namespace Avalonia.Controls
_iScreenImpl = iScreenImpl;
}
public Screen ScreenFromBounds(Rect bounds){
public Screen ScreenFromBounds(PixelRect bounds){
Screen currMaxScreen = null;
double maxAreaSize = 0;
@ -39,16 +39,16 @@ namespace Avalonia.Controls
return currMaxScreen;
}
public Screen ScreenFromPoint(Point point)
public Screen ScreenFromPoint(PixelPoint point)
{
return All.FirstOrDefault(x=>x.Bounds.Contains(point));
return All.FirstOrDefault(x => x.Bounds.Contains(point));
}
public Screen ScreenFromVisual(IVisual visual)
{
Point tl = visual.PointToScreen(visual.Bounds.TopLeft);
Point br = visual.PointToScreen(visual.Bounds.BottomRight);
return ScreenFromBounds(new Rect(tl,br));
var tl = visual.PointToScreen(visual.Bounds.TopLeft);
var br = visual.PointToScreen(visual.Bounds.BottomRight);
return ScreenFromBounds(new PixelRect(tl, br));
}
}
}
}

8
src/Avalonia.Controls/TopLevel.cs

@ -233,15 +233,15 @@ namespace Avalonia.Controls
}
/// <inheritdoc/>
Point IRenderRoot.PointToClient(Point p)
Point IRenderRoot.PointToClient(PixelPoint p)
{
return PlatformImpl?.PointToClient(p) ?? default(Point);
return PlatformImpl?.PointToClient(p) ?? default;
}
/// <inheritdoc/>
Point IRenderRoot.PointToScreen(Point p)
PixelPoint IRenderRoot.PointToScreen(Point p)
{
return PlatformImpl?.PointToScreen(p) ?? default(Point);
return PlatformImpl?.PointToScreen(p) ?? default;
}
/// <summary>

2
src/Avalonia.Controls/Viewbox.cs

@ -12,7 +12,7 @@ namespace Avalonia.Controls
/// <summary>
/// The stretch property
/// </summary>
public static AvaloniaProperty<Stretch> StretchProperty =
public static readonly AvaloniaProperty<Stretch> StretchProperty =
AvaloniaProperty.RegisterDirect<Viewbox, Stretch>(nameof(Stretch),
v => v.Stretch, (c, v) => c.Stretch = v, Stretch.Uniform);

58
src/Avalonia.Controls/Window.cs

@ -388,45 +388,48 @@ namespace Avalonia.Controls
PlatformImpl?.Show();
Renderer?.Start();
}
SetWindowStartupLocation();
SetWindowStartupLocation(Owner?.PlatformImpl);
}
/// <summary>
/// Shows the window as a dialog.
/// </summary>
/// <param name="owner">The dialog's owner window.</param>
/// <returns>
/// A task that can be used to track the lifetime of the dialog.
/// </returns>
public Task ShowDialog(Window parent)
public Task ShowDialog(Window owner)
{
return ShowDialog<object>(parent);
return ShowDialog<object>(owner);
}
/// <summary>
/// Shows the window as a dialog.
/// </summary>
/// <typeparam name="TResult">
/// The type of the result produced by the dialog.
/// </typeparam>
/// <param name="owner">The dialog's owner window.</param>
/// <returns>.
/// A task that can be used to retrieve the result of the dialog when it closes.
/// </returns>
public Task<TResult> ShowDialog<TResult>(Window parent) => ShowDialog<TResult>(parent.PlatformImpl);
public Task<TResult> ShowDialog<TResult>(Window owner) => ShowDialog<TResult>(owner.PlatformImpl);
/// <summary>
/// Shows the window as a dialog.
/// </summary>
/// <typeparam name="TResult">
/// The type of the result produced by the dialog.
/// </typeparam>
/// <param name="owner">The dialog's owner window.</param>
/// <returns>.
/// A task that can be used to retrieve the result of the dialog when it closes.
/// </returns>
public Task<TResult> ShowDialog<TResult>(IWindowImpl parent)
public Task<TResult> ShowDialog<TResult>(IWindowImpl owner)
{
if(parent == null)
throw new ArgumentNullException(nameof(parent));
if(owner == null)
throw new ArgumentNullException(nameof(owner));
if (IsVisible)
{
throw new InvalidOperationException("The window is already being shown.");
@ -435,15 +438,15 @@ namespace Avalonia.Controls
AddWindow(this);
EnsureInitialized();
SetWindowStartupLocation();
IsVisible = true;
LayoutManager.ExecuteInitialLayoutPass(this);
var result = new TaskCompletionSource<TResult>();
using (BeginAutoSizing())
{
PlatformImpl?.ShowDialog(parent);
var result = new TaskCompletionSource<TResult>();
PlatformImpl?.ShowDialog(owner);
Renderer?.Start();
Observable.FromEventPattern<EventHandler, EventArgs>(
@ -452,29 +455,42 @@ namespace Avalonia.Controls
.Take(1)
.Subscribe(_ =>
{
parent.Activate();
owner.Activate();
result.SetResult((TResult)(_dialogResult ?? default(TResult)));
});
return result.Task;
}
SetWindowStartupLocation(owner);
return result.Task;
}
void SetWindowStartupLocation()
private void SetWindowStartupLocation(IWindowBaseImpl owner = null)
{
var scaling = owner?.Scaling ?? PlatformImpl?.Scaling ?? 1;
// TODO: We really need non-client size here.
var rect = new PixelRect(
PixelPoint.Origin,
PixelSize.FromSize(ClientSize, scaling));
if (WindowStartupLocation == WindowStartupLocation.CenterScreen)
{
var screen = Screens.ScreenFromPoint(Bounds.Position);
var screen = Screens.ScreenFromPoint(owner?.Position ?? Position);
if (screen != null)
Position = screen.WorkingArea.CenterRect(new Rect(ClientSize)).Position;
{
Position = screen.WorkingArea.CenterRect(rect).Position;
}
}
else if (WindowStartupLocation == WindowStartupLocation.CenterOwner)
{
if (Owner != null)
if (owner != null)
{
var positionAsSize = Owner.ClientSize / 2 - ClientSize / 2;
Position = Owner.Position + new Point(positionAsSize.Width, positionAsSize.Height);
// TODO: We really need non-client size here.
var ownerRect = new PixelRect(
owner.Position,
PixelSize.FromSize(owner.ClientSize, scaling));
Position = ownerRect.CenterRect(rect).Position;
}
}
}

10
src/Avalonia.Controls/WindowBase.cs

@ -81,7 +81,7 @@ namespace Avalonia.Controls
/// <summary>
/// Fired when the window position is changed.
/// </summary>
public event EventHandler<PointEventArgs> PositionChanged;
public event EventHandler<PixelPointEventArgs> PositionChanged;
[CanBeNull]
public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl;
@ -98,9 +98,9 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets the window position in screen coordinates.
/// </summary>
public Point Position
public PixelPoint Position
{
get { return PlatformImpl?.Position ?? default(Point); }
get { return PlatformImpl?.Position ?? PixelPoint.Origin; }
set
{
if (PlatformImpl is IWindowBaseImpl impl)
@ -267,9 +267,9 @@ namespace Avalonia.Controls
/// <see cref="IWindowBaseImpl.PositionChanged"/>.
/// </summary>
/// <param name="pos">The window position.</param>
private void HandlePositionChanged(Point pos)
private void HandlePositionChanged(PixelPoint pos)
{
PositionChanged?.Invoke(this, new PointEventArgs(pos));
PositionChanged?.Invoke(this, new PixelPointEventArgs(pos));
}
/// <summary>

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

@ -35,8 +35,8 @@ namespace Avalonia.DesignerSupport.Remote
{
}
public Point Position { get; set; }
public Action<Point> PositionChanged { get; set; }
public PixelPoint Position { get; set; }
public Action<PixelPoint> PositionChanged { get; set; }
public Action Deactivated { get; set; }
public Action Activated { get; set; }
public Func<bool> Closing { get; set; }

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

@ -29,8 +29,8 @@ namespace Avalonia.DesignerSupport.Remote
public Func<bool> Closing { get; set; }
public Action Closed { get; set; }
public IMouseDevice MouseDevice { get; } = new MouseDevice();
public Point Position { get; set; }
public Action<Point> PositionChanged { get; set; }
public PixelPoint Position { get; set; }
public Action<PixelPoint> PositionChanged { get; set; }
public WindowState WindowState { get; set; }
public Action<WindowState> WindowStateChanged { get; set; }
public IRenderer CreateRenderer(IRenderRoot root) => new ImmediateRenderer(root);
@ -45,9 +45,9 @@ namespace Avalonia.DesignerSupport.Remote
{
}
public Point PointToClient(Point point) => point;
public Point PointToClient(PixelPoint p) => p.ToPoint(1);
public Point PointToScreen(Point point) => point;
public PixelPoint PointToScreen(Point p) => PixelPoint.FromPoint(p, 1);
public void SetCursor(IPlatformHandle cursor)
{
@ -157,6 +157,6 @@ namespace Avalonia.DesignerSupport.Remote
public int ScreenCount => 1;
public Screen[] AllScreens { get; } =
{new Screen(new Rect(0, 0, 4000, 4000), new Rect(0, 0, 4000, 4000), true)};
{new Screen(new PixelRect(0, 0, 4000, 4000), new PixelRect(0, 0, 4000, 4000), true)};
}
}

6
src/Avalonia.Desktop/AppBuilderDesktopExtensions.cs

@ -29,7 +29,7 @@ namespace Avalonia
}
else
{
LoadGtk3(builder);
LoadX11(builder);
LoadSkia(builder);
}
return builder;
@ -42,9 +42,9 @@ namespace Avalonia
where TAppBuilder : AppBuilderBase<TAppBuilder>, new()
=> builder.UseWin32();
static void LoadGtk3<TAppBuilder>(TAppBuilder builder)
static void LoadX11<TAppBuilder>(TAppBuilder builder)
where TAppBuilder : AppBuilderBase<TAppBuilder>, new()
=> builder.UseGtk3();
=> builder.UseX11();
static void LoadDirect2D1<TAppBuilder>(TAppBuilder builder)
where TAppBuilder : AppBuilderBase<TAppBuilder>, new()

4
src/Avalonia.Desktop/Avalonia.Desktop.csproj

@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../src/Windows/Avalonia.Win32/Avalonia.Win32.csproj" />
<ProjectReference Include="../../src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj" />
<ProjectReference Include="../../src/Skia/Avalonia.Skia/Avalonia.Skia.csproj" />
<ProjectReference Include="../../src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj" />
<ProjectReference Include="../../src/Avalonia.Native/Avalonia.Native.csproj" />
<ProjectReference Include="../../packages/Avalonia/Avalonia.csproj" />
<ProjectReference Include="../Avalonia.X11/Avalonia.X11.csproj" />
</ItemGroup>
</Project>

2
src/Avalonia.Input/Cursors.cs

@ -47,7 +47,7 @@ namespace Avalonia.Input
public class Cursor
{
public static Cursor Default = new Cursor(StandardCursorType.Arrow);
public static readonly Cursor Default = new Cursor(StandardCursorType.Arrow);
internal Cursor(IPlatformHandle platformCursor)
{

6
src/Avalonia.Input/DataFormats.cs

@ -5,11 +5,11 @@
/// <summary>
/// Dataformat for plaintext
/// </summary>
public static string Text = nameof(Text);
public static readonly string Text = nameof(Text);
/// <summary>
/// Dataformat for one or more filenames
/// </summary>
public static string FileNames = nameof(FileNames);
public static readonly string FileNames = nameof(FileNames);
}
}
}

10
src/Avalonia.Input/DragDrop.cs

@ -9,21 +9,21 @@ namespace Avalonia.Input
/// <summary>
/// Event which is raised, when a drag-and-drop operation enters the element.
/// </summary>
public static RoutedEvent<DragEventArgs> DragEnterEvent = RoutedEvent.Register<DragEventArgs>("DragEnter", RoutingStrategies.Bubble, typeof(DragDrop));
public static readonly RoutedEvent<DragEventArgs> DragEnterEvent = RoutedEvent.Register<DragEventArgs>("DragEnter", RoutingStrategies.Bubble, typeof(DragDrop));
/// <summary>
/// Event which is raised, when a drag-and-drop operation leaves the element.
/// </summary>
public static RoutedEvent<RoutedEventArgs> DragLeaveEvent = RoutedEvent.Register<RoutedEventArgs>("DragLeave", RoutingStrategies.Bubble, typeof(DragDrop));
public static readonly RoutedEvent<RoutedEventArgs> DragLeaveEvent = RoutedEvent.Register<RoutedEventArgs>("DragLeave", RoutingStrategies.Bubble, typeof(DragDrop));
/// <summary>
/// Event which is raised, when a drag-and-drop operation is updated while over the element.
/// </summary>
public static RoutedEvent<DragEventArgs> DragOverEvent = RoutedEvent.Register<DragEventArgs>("DragOver", RoutingStrategies.Bubble, typeof(DragDrop));
public static readonly RoutedEvent<DragEventArgs> DragOverEvent = RoutedEvent.Register<DragEventArgs>("DragOver", RoutingStrategies.Bubble, typeof(DragDrop));
/// <summary>
/// Event which is raised, when a drag-and-drop operation should complete over the element.
/// </summary>
public static RoutedEvent<DragEventArgs> DropEvent = RoutedEvent.Register<DragEventArgs>("Drop", RoutingStrategies.Bubble, typeof(DragDrop));
public static readonly RoutedEvent<DragEventArgs> DropEvent = RoutedEvent.Register<DragEventArgs>("Drop", RoutingStrategies.Bubble, typeof(DragDrop));
public static AvaloniaProperty<bool> AllowDropProperty = AvaloniaProperty.RegisterAttached<Interactive, bool>("AllowDrop", typeof(DragDrop), inherits: true);
public static readonly AvaloniaProperty<bool> AllowDropProperty = AvaloniaProperty.RegisterAttached<Interactive, bool>("AllowDrop", typeof(DragDrop), inherits: true);
/// <summary>
/// Gets a value indicating whether the given element can be used as the target of a drag-and-drop operation.

2
src/Avalonia.Input/IMouseDevice.cs

@ -11,6 +11,6 @@ namespace Avalonia.Input
/// <summary>
/// Gets the mouse position, in screen coordinates.
/// </summary>
Point Position { get; }
PixelPoint Position { get; }
}
}

6
src/Avalonia.Input/MouseDevice.cs

@ -18,7 +18,7 @@ namespace Avalonia.Input
{
private int _clickCount;
private Rect _lastClickRect;
private uint _lastClickTime;
private ulong _lastClickTime;
private IInputElement _captured;
private IDisposable _capturedSubscription;
@ -54,7 +54,7 @@ namespace Avalonia.Input
/// <summary>
/// Gets the mouse position, in screen coordinates.
/// </summary>
public Point Position
public PixelPoint Position
{
get;
protected set;
@ -152,7 +152,7 @@ namespace Avalonia.Input
ClearPointerOver(this, root);
}
private bool MouseDown(IMouseDevice device, uint timestamp, IInputElement root, Point p, MouseButton button, InputModifiers inputModifiers)
private bool MouseDown(IMouseDevice device, ulong timestamp, IInputElement root, Point p, MouseButton button, InputModifiers inputModifiers)
{
Contract.Requires<ArgumentNullException>(device != null);
Contract.Requires<ArgumentNullException>(root != null);

2
src/Avalonia.Input/Raw/RawDragEvent.cs

@ -3,7 +3,7 @@
public class RawDragEvent : RawInputEventArgs
{
public IInputElement InputRoot { get; }
public Point Location { get; }
public Point Location { get; set; }
public IDataObject Data { get; }
public DragDropEffects Effects { get; set; }
public RawDragEventType Type { get; }

4
src/Avalonia.Input/Raw/RawInputEventArgs.cs

@ -21,7 +21,7 @@ namespace Avalonia.Input.Raw
/// </summary>
/// <param name="device">The associated device.</param>
/// <param name="timestamp">The event timestamp.</param>
public RawInputEventArgs(IInputDevice device, uint timestamp)
public RawInputEventArgs(IInputDevice device, ulong timestamp)
{
Contract.Requires<ArgumentNullException>(device != null);
@ -47,6 +47,6 @@ namespace Avalonia.Input.Raw
/// <summary>
/// Gets the timestamp associated with the event.
/// </summary>
public uint Timestamp { get; private set; }
public ulong Timestamp { get; private set; }
}
}

2
src/Avalonia.Input/Raw/RawKeyEventArgs.cs

@ -13,7 +13,7 @@ namespace Avalonia.Input.Raw
{
public RawKeyEventArgs(
IKeyboardDevice device,
uint timestamp,
ulong timestamp,
RawKeyEventType type,
Key key, InputModifiers modifiers)
: base(device, timestamp)

2
src/Avalonia.Input/Raw/RawMouseEventArgs.cs

@ -35,7 +35,7 @@ namespace Avalonia.Input.Raw
/// <param name="inputModifiers">The input modifiers.</param>
public RawMouseEventArgs(
IInputDevice device,
uint timestamp,
ulong timestamp,
IInputRoot root,
RawMouseEventType type,
Point position,

2
src/Avalonia.Input/Raw/RawMouseWheelEventArgs.cs

@ -8,7 +8,7 @@ namespace Avalonia.Input.Raw
{
public RawMouseWheelEventArgs(
IInputDevice device,
uint timestamp,
ulong timestamp,
IInputRoot root,
Point position,
Vector delta, InputModifiers inputModifiers)

2
src/Avalonia.Input/Raw/RawTextInputEventArgs.cs

@ -7,7 +7,7 @@ namespace Avalonia.Input.Raw
{
public string Text { get; set; }
public RawTextInputEventArgs(IKeyboardDevice device, uint timestamp, string text) : base(device, timestamp)
public RawTextInputEventArgs(IKeyboardDevice device, ulong timestamp, string text) : base(device, timestamp)
{
Text = text;
}

15
src/Avalonia.Native/Helpers.cs

@ -12,11 +12,21 @@ namespace Avalonia.Native
return new Point(pt.X, pt.Y);
}
public static PixelPoint ToAvaloniaPixelPoint(this AvnPoint pt)
{
return new PixelPoint((int)pt.X, (int)pt.Y);
}
public static AvnPoint ToAvnPoint (this Point pt)
{
return new AvnPoint { X = pt.X, Y = pt.Y };
}
public static AvnPoint ToAvnPoint(this PixelPoint pt)
{
return new AvnPoint { X = pt.X, Y = pt.Y };
}
public static AvnSize ToAvnSize (this Size size)
{
return new AvnSize { Height = size.Height, Width = size.Width };
@ -31,5 +41,10 @@ namespace Avalonia.Native
{
return new Rect(rect.X, rect.Y, rect.Width, rect.Height);
}
public static PixelRect ToAvaloniaPixelRect(this AvnRect rect)
{
return new PixelRect((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height);
}
}
}

5
src/Avalonia.Native/ScreenImpl.cs

@ -29,7 +29,10 @@ namespace Avalonia.Native
{
var screen = _native.GetScreen(i);
result[i] = new Screen(screen.Bounds.ToAvaloniaRect(), screen.WorkingArea.ToAvaloniaRect(), screen.Primary);
result[i] = new Screen(
screen.Bounds.ToAvaloniaPixelRect(),
screen.WorkingArea.ToAvaloniaPixelRect(),
screen.Primary);
}
return result;

14
src/Avalonia.Native/WindowImplBase.cs

@ -159,7 +159,7 @@ namespace Avalonia.Native
void IAvnWindowBaseEvents.PositionChanged(AvnPoint position)
{
_parent.PositionChanged?.Invoke(position.ToAvaloniaPoint());
_parent.PositionChanged?.Invoke(position.ToAvaloniaPixelPoint());
}
void IAvnWindowBaseEvents.RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
@ -275,20 +275,20 @@ namespace Avalonia.Native
}
public Point Position
public PixelPoint Position
{
get => _native.GetPosition().ToAvaloniaPoint();
get => _native.GetPosition().ToAvaloniaPixelPoint();
set => _native.SetPosition(value.ToAvnPoint());
}
public Point PointToClient(Point point)
public Point PointToClient(PixelPoint point)
{
return _native.PointToClient(point.ToAvnPoint()).ToAvaloniaPoint();
}
public Point PointToScreen(Point point)
public PixelPoint PointToScreen(Point point)
{
return _native.PointToScreen(point.ToAvnPoint()).ToAvaloniaPoint();
return _native.PointToScreen(point.ToAvnPoint()).ToAvaloniaPixelPoint();
}
public void Hide()
@ -320,7 +320,7 @@ namespace Avalonia.Native
_native.Cursor = newCursor.Cursor;
}
public Action<Point> PositionChanged { get; set; }
public Action<PixelPoint> PositionChanged { get; set; }
public Action<RawInputEventArgs> Input { get; set; }

6
src/Avalonia.OpenGL/EglContext.cs

@ -31,14 +31,14 @@ namespace Avalonia.OpenGL
public void MakeCurrent()
{
if (!_egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, Context))
throw new OpenGlException("eglMakeCurrent failed");
throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl);
}
public void MakeCurrent(EglSurface surface)
{
var surf = ((EglSurface)surface)?.DangerousGetHandle() ?? OffscreenSurface;
var surf = surface?.DangerousGetHandle() ?? OffscreenSurface;
if (!_egl.MakeCurrent(_disp.Handle, surf, surf, Context))
throw new OpenGlException("eglMakeCurrent failed");
throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl);
}
}
}

17
src/Avalonia.OpenGL/EglDisplay.cs

@ -1,5 +1,4 @@
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using Avalonia.Platform.Interop;
using static Avalonia.OpenGL.EglConsts;
@ -34,11 +33,11 @@ namespace Avalonia.OpenGL
if (_display == IntPtr.Zero)
_display = _egl.GetDisplay(IntPtr.Zero);
if(_display == IntPtr.Zero)
throw new OpenGlException("eglGetDisplay failed");
if (_display == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglGetDisplay", _egl);
if (!_egl.Initialize(_display, out var major, out var minor))
throw new OpenGlException("eglInitialize failed");
throw OpenGlException.GetFormattedException("eglInitialize", _egl);
foreach (var cfg in new[]
{
@ -113,7 +112,7 @@ namespace Avalonia.OpenGL
var shareCtx = (EglContext)share;
var ctx = _egl.CreateContext(_display, _config, shareCtx?.Context ?? IntPtr.Zero, _contextAttributes);
if (ctx == IntPtr.Zero)
throw new OpenGlException("eglCreateContext failed");
throw OpenGlException.GetFormattedException("eglCreateContext", _egl);
var surf = _egl.CreatePBufferSurface(_display, _config, new[]
{
EGL_WIDTH, 1,
@ -121,7 +120,7 @@ namespace Avalonia.OpenGL
EGL_NONE
});
if (surf == IntPtr.Zero)
throw new OpenGlException("eglCreatePbufferSurface failed");
throw OpenGlException.GetFormattedException("eglCreatePBufferSurface", _egl);
var rv = new EglContext(this, _egl, ctx, surf);
rv.MakeCurrent(null);
return rv;
@ -130,14 +129,14 @@ namespace Avalonia.OpenGL
public void ClearContext()
{
if (!_egl.MakeCurrent(_display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero))
throw new OpenGlException("eglMakeCurrent failed");
throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl);
}
public EglSurface CreateWindowSurface(IntPtr window)
{
var s = _egl.CreateWindowSurface(_display, _config, window, new[] {EGL_NONE, EGL_NONE});
if (s == IntPtr.Zero)
throw new OpenGlException("eglCreateWindowSurface failed");
throw OpenGlException.GetFormattedException("eglCreateWindowSurface", _egl);
return new EglSurface(this, _egl, s);
}

21
src/Avalonia.OpenGL/EglErrors.cs

@ -0,0 +1,21 @@
namespace Avalonia.OpenGL
{
public enum EglErrors
{
EGL_SUCCESS = EglConsts.EGL_SUCCESS,
EGL_NOT_INITIALIZED = EglConsts.EGL_NOT_INITIALIZED,
EGL_BAD_ACCESS = EglConsts.EGL_BAD_ACCESS,
EGL_BAD_ALLOC = EglConsts.EGL_BAD_ALLOC,
EGL_BAD_ATTRIBUTE = EglConsts.EGL_BAD_ATTRIBUTE,
EGL_BAD_CONTEXT = EglConsts.EGL_BAD_CONTEXT,
EGL_BAD_CONFIG = EglConsts.EGL_BAD_CONFIG,
EGL_BAD_CURRENT_SURFACE = EglConsts.EGL_BAD_CURRENT_SURFACE,
EGL_BAD_DISPLAY = EglConsts.EGL_BAD_DISPLAY,
EGL_BAD_SURFACE = EglConsts.EGL_BAD_SURFACE,
EGL_BAD_MATCH = EglConsts.EGL_BAD_MATCH,
EGL_BAD_PARAMETER = EglConsts.EGL_BAD_PARAMETER,
EGL_BAD_NATIVE_PIXMAP = EglConsts.EGL_BAD_NATIVE_PIXMAP,
EGL_BAD_NATIVE_WINDOW = EglConsts.EGL_BAD_NATIVE_WINDOW,
EGL_CONTEXT_LOST = EglConsts.EGL_CONTEXT_LOST
}
}

7
src/Avalonia.OpenGL/EglInterface.cs

@ -1,7 +1,6 @@
using System;
using Avalonia.Platform;
using Avalonia.Platform.Interop;
using static Avalonia.OpenGL.EglConsts;
namespace Avalonia.OpenGL
{
@ -32,8 +31,12 @@ namespace Avalonia.OpenGL
var lib = dyn.LoadLibrary(library);
return (s, o) => dyn.GetProcAddress(lib, s, o);
}
// ReSharper disable UnassignedGetOnlyAutoProperty
public delegate int EglGetError();
[EntryPoint("eglGetError")]
public EglGetError GetError { get; }
public delegate IntPtr EglGetDisplay(IntPtr nativeDisplay);
[EntryPoint("eglGetDisplay")]
public EglGetDisplay GetDisplay { get; }

3
src/Avalonia.OpenGL/GlConsts.cs

@ -456,12 +456,15 @@ namespace Avalonia.OpenGL
public const int GL_RENDERER = 0x1F01;
public const int GL_VERSION = 0x1F02;
public const int GL_EXTENSIONS = 0x1F03;
public const int GL_NO_ERROR = 0;
public const int GL_INVALID_ENUM = 0x0500;
public const int GL_INVALID_VALUE = 0x0501;
public const int GL_INVALID_OPERATION = 0x0502;
public const int GL_STACK_OVERFLOW = 0x0503;
public const int GL_STACK_UNDERFLOW = 0x0504;
public const int GL_OUT_OF_MEMORY = 0x0505;
public const int GL_INVALID_FRAMEBUFFER_OPERATION = 0x0506;
public const int GL_CONTEXT_LOST = 0x0507;
public const int GL_CURRENT_BIT = 0x00000001;
public const int GL_POINT_BIT = 0x00000002;
public const int GL_LINE_BIT = 0x00000004;

15
src/Avalonia.OpenGL/GlErrors.cs

@ -0,0 +1,15 @@
namespace Avalonia.OpenGL
{
public enum GlErrors
{
GL_NO_ERROR = GlConsts.GL_NO_ERROR,
GL_INVALID_ENUM = GlConsts.GL_INVALID_ENUM,
GL_INVALID_VALUE = GlConsts.GL_INVALID_VALUE,
GL_INVALID_OPERATION = GlConsts.GL_INVALID_OPERATION,
GL_INVALID_FRAMEBUFFER_OPERATION = GlConsts.GL_INVALID_FRAMEBUFFER_OPERATION,
GL_STACK_OVERFLOW = GlConsts.GL_STACK_OVERFLOW,
GL_STACK_UNDERFLOW = GlConsts.GL_STACK_UNDERFLOW,
GL_OUT_OF_MEMORY = GlConsts.GL_OUT_OF_MEMORY,
GL_CONTEXT_LOST = GlConsts.GL_CONTEXT_LOST
}
}

5
src/Avalonia.OpenGL/GlInterface.cs

@ -19,7 +19,10 @@ namespace Avalonia.OpenGL
public T GetProcAddress<T>(string proc) => Marshal.GetDelegateForFunctionPointer<T>(GetProcAddress(proc));
// ReSharper disable UnassignedGetOnlyAutoProperty
public delegate int GlGetError();
[EntryPoint("glGetError")]
public GlGetError GetError { get; }
public delegate void GlClearStencil(int s);
[EntryPoint("glClearStencil")]
public GlClearStencil ClearStencil { get; }

30
src/Avalonia.OpenGL/OpenGlException.cs

@ -4,9 +4,39 @@ namespace Avalonia.OpenGL
{
public class OpenGlException : Exception
{
public int? ErrorCode { get; private set; }
public OpenGlException(string message) : base(message)
{
}
private OpenGlException(string message, int errorCode) : base(message)
{
ErrorCode = errorCode;
}
public static OpenGlException GetFormattedException(string funcName, EglInterface egl)
{
return GetFormattedException(typeof(EglErrors), funcName, egl.GetError());
}
public static OpenGlException GetFormattedException(string funcName, GlInterface gl)
{
return GetFormattedException(typeof(GlErrors), funcName, gl.GetError());
}
private static OpenGlException GetFormattedException(Type consts, string funcName, int errorCode)
{
try
{
string errorName = Enum.GetName(consts, errorCode);
return new OpenGlException(
$"{funcName} failed with error {errorName} (0x{errorCode.ToString("X")})", errorCode);
}
catch (ArgumentException)
{
return new OpenGlException($"{funcName} failed with error 0x{errorCode.ToString("X")}", errorCode);
}
}
}
}

20
src/Avalonia.Styling/Styling/ChildSelector.cs

@ -24,6 +24,9 @@ namespace Avalonia.Styling
/// <inheritdoc/>
public override bool InTemplate => _parent.InTemplate;
/// <inheritdoc/>
public override bool IsCombinator => true;
/// <inheritdoc/>
public override Type TargetType => null;
@ -43,11 +46,24 @@ namespace Avalonia.Styling
if (controlParent != null)
{
return _parent.Match((IStyleable)controlParent, subscribe);
var parentMatch = _parent.Match((IStyleable)controlParent, subscribe);
if (parentMatch.Result == SelectorMatchResult.Sometimes)
{
return parentMatch;
}
else if (parentMatch.IsMatch)
{
return SelectorMatch.AlwaysThisInstance;
}
else
{
return SelectorMatch.NeverThisInstance;
}
}
else
{
return SelectorMatch.False;
return SelectorMatch.NeverThisInstance;
}
}

16
src/Avalonia.Styling/Styling/DescendentSelector.cs

@ -22,6 +22,9 @@ namespace Avalonia.Styling
_parent = parent;
}
/// <inheritdoc/>
public override bool IsCombinator => true;
/// <inheritdoc/>
public override bool InTemplate => _parent.InTemplate;
@ -51,16 +54,13 @@ namespace Avalonia.Styling
{
var match = _parent.Match((IStyleable)c, subscribe);
if (match.ImmediateResult != null)
if (match.Result == SelectorMatchResult.Sometimes)
{
if (match.ImmediateResult == true)
{
return SelectorMatch.True;
}
descendantMatches.Add(match.Activator);
}
else
else if (match.IsMatch)
{
descendantMatches.Add(match.ObservableResult);
return SelectorMatch.AlwaysThisInstance;
}
}
}
@ -71,7 +71,7 @@ namespace Avalonia.Styling
}
else
{
return SelectorMatch.False;
return SelectorMatch.NeverThisInstance;
}
}

7
src/Avalonia.Styling/Styling/IStyle.cs

@ -17,7 +17,12 @@ namespace Avalonia.Styling
/// <param name="container">
/// The control that contains this style. May be null.
/// </param>
void Attach(IStyleable control, IStyleHost container);
/// <returns>
/// True if the style can match a control of type <paramref name="control"/>
/// (even if it does not match this control specifically); false if the style
/// can never match.
/// </returns>
bool Attach(IStyleable control, IStyleHost container);
void Detach();
}

6
src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs

@ -30,6 +30,9 @@ namespace Avalonia.Styling
/// <inheritdoc/>
public override bool InTemplate => _previous?.InTemplate ?? false;
/// <inheritdoc/>
public override bool IsCombinator => false;
/// <summary>
/// Gets the name of the control to match.
/// </summary>
@ -78,7 +81,8 @@ namespace Avalonia.Styling
}
else
{
return new SelectorMatch((control.GetValue(_property) ?? string.Empty).Equals(_value));
var result = (control.GetValue(_property) ?? string.Empty).Equals(_value);
return result ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance;
}
}

39
src/Avalonia.Styling/Styling/Selector.cs

@ -17,6 +17,14 @@ namespace Avalonia.Styling
/// </summary>
public abstract bool InTemplate { get; }
/// <summary>
/// Gets a value indicating whether this selector is a combinator.
/// </summary>
/// <remarks>
/// A combinator is a selector such as Child or Descendent which links simple selectors.
/// </remarks>
public abstract bool IsCombinator { get; }
/// <summary>
/// Gets the target type of the selector, if available.
/// </summary>
@ -33,25 +41,32 @@ namespace Avalonia.Styling
/// <returns>A <see cref="SelectorMatch"/>.</returns>
public SelectorMatch Match(IStyleable control, bool subscribe = true)
{
List<IObservable<bool>> inputs = new List<IObservable<bool>>();
Selector selector = this;
var inputs = new List<IObservable<bool>>();
var selector = this;
var alwaysThisType = true;
var hitCombinator = false;
while (selector != null)
{
if (selector.InTemplate && control.TemplatedParent == null)
{
return SelectorMatch.False;
}
hitCombinator |= selector.IsCombinator;
var match = selector.Evaluate(control, subscribe);
if (match.ImmediateResult == false)
if (!match.IsMatch)
{
return hitCombinator ? SelectorMatch.NeverThisInstance : match;
}
else if (selector.InTemplate && control.TemplatedParent == null)
{
return SelectorMatch.NeverThisInstance;
}
else if (match.Result == SelectorMatchResult.AlwaysThisInstance)
{
return match;
alwaysThisType = false;
}
else if (match.ObservableResult != null)
else if (match.Result == SelectorMatchResult.Sometimes)
{
inputs.Add(match.ObservableResult);
inputs.Add(match.Activator);
}
selector = selector.MovePrevious();
@ -63,7 +78,9 @@ namespace Avalonia.Styling
}
else
{
return SelectorMatch.True;
return alwaysThisType && !hitCombinator ?
SelectorMatch.AlwaysThisType :
SelectorMatch.AlwaysThisInstance;
}
}

86
src/Avalonia.Styling/Styling/SelectorMatch.cs

@ -5,51 +5,95 @@ using System;
namespace Avalonia.Styling
{
/// <summary>
/// Describes how a <see cref="SelectorMatch"/> matches a control and its type.
/// </summary>
public enum SelectorMatchResult
{
/// <summary>
/// The selector never matches this type.
/// </summary>
NeverThisType,
/// <summary>
/// The selector never matches this instance, but can match this type.
/// </summary>
NeverThisInstance,
/// <summary>
/// The selector always matches this type.
/// </summary>
AlwaysThisType,
/// <summary>
/// The selector always matches this instance, but doesn't always match this type.
/// </summary>
AlwaysThisInstance,
/// <summary>
/// The selector matches this instance based on the <see cref="SelectorMatch.Activator"/>.
/// </summary>
Sometimes,
}
/// <summary>
/// Holds the result of a <see cref="Selector"/> match.
/// </summary>
/// <remarks>
/// There are two types of selectors - ones whose match can never change for a particular
/// control (such as <see cref="Selectors.OfType(Selector, Type)"/>) and ones whose result can
/// change over time (such as <see cref="Selectors.Class(Selector, string)"/>. For the first
/// category of selectors, the value of <see cref="ImmediateResult"/> will be set but for the
/// second, <see cref="ImmediateResult"/> will be null and <see cref="ObservableResult"/> will
/// hold an observable which tracks the match.
/// A selector match describes whether and how a <see cref="Selector"/> matches a control, and
/// in addition whether the selector can ever match a control of the same type.
/// </remarks>
public class SelectorMatch
{
public static readonly SelectorMatch False = new SelectorMatch(false);
/// <summary>
/// A selector match with the result of <see cref="SelectorMatchResult.NeverThisType"/>.
/// </summary>
public static readonly SelectorMatch NeverThisType = new SelectorMatch(SelectorMatchResult.NeverThisType);
public static readonly SelectorMatch True = new SelectorMatch(true);
/// <summary>
/// A selector match with the result of <see cref="SelectorMatchResult.NeverThisInstance"/>.
/// </summary>
public static readonly SelectorMatch NeverThisInstance = new SelectorMatch(SelectorMatchResult.NeverThisInstance);
/// <summary>
/// Initializes a new instance of the <see cref="SelectorMatch"/> class.
/// A selector match with the result of <see cref="SelectorMatchResult.AlwaysThisType"/>.
/// </summary>
/// <param name="match">The immediate match value.</param>
public SelectorMatch(bool match)
{
ImmediateResult = match;
}
public static readonly SelectorMatch AlwaysThisType = new SelectorMatch(SelectorMatchResult.AlwaysThisType);
/// <summary>
/// Initializes a new instance of the <see cref="SelectorMatch"/> class.
/// Gets a selector match with the result of <see cref="SelectorMatchResult.AlwaysThisInstance"/>.
/// </summary>
/// <param name="match">The observable match value.</param>
public static readonly SelectorMatch AlwaysThisInstance = new SelectorMatch(SelectorMatchResult.AlwaysThisInstance);
/// <summary>
/// Initializes a new instance of the <see cref="SelectorMatch"/> class with a
/// <see cref="SelectorMatchResult.Sometimes"/> result.
/// </summary>
/// <param name="match">The match activator.</param>
public SelectorMatch(IObservable<bool> match)
{
ObservableResult = match;
Contract.Requires<ArgumentNullException>(match != null);
Result = SelectorMatchResult.Sometimes;
Activator = match;
}
private SelectorMatch(SelectorMatchResult result) => Result = result;
/// <summary>
/// Gets the immediate result of the selector match, in the case of selectors that cannot
/// change over time.
/// Gets a value indicating whether the match was positive.
/// </summary>
public bool IsMatch => Result >= SelectorMatchResult.AlwaysThisType;
/// <summary>
/// Gets the result of the match.
/// </summary>
public bool? ImmediateResult { get; }
public SelectorMatchResult Result { get; }
/// <summary>
/// Gets an observable which tracks the selector match, in the case of selectors that can
/// change over time.
/// </summary>
public IObservable<bool> ObservableResult { get; }
public IObservable<bool> Activator { get; }
}
}

2
src/Avalonia.Styling/Styling/Setter.cs

@ -69,7 +69,7 @@ namespace Avalonia.Styling
{
throw new ArgumentException(
"Cannot assign a control to Setter.Value. Wrap the control in a <Template>.",
"value");
nameof(value));
}
_value = value;

24
src/Avalonia.Styling/Styling/Style.cs

@ -106,20 +106,14 @@ namespace Avalonia.Styling
/// <inheritdoc/>
bool IResourceProvider.HasResources => _resources?.Count > 0;
/// <summary>
/// Attaches the style to a control if the style's selector matches.
/// </summary>
/// <param name="control">The control to attach to.</param>
/// <param name="container">
/// The control that contains this style. May be null.
/// </param>
public void Attach(IStyleable control, IStyleHost container)
/// <inheritdoc/>
public bool Attach(IStyleable control, IStyleHost container)
{
if (Selector != null)
{
var match = Selector.Match(control);
if (match.ImmediateResult != false)
if (match.IsMatch)
{
var controlSubscriptions = GetSubscriptions(control);
@ -129,9 +123,10 @@ namespace Avalonia.Styling
{
foreach (var animation in Animations)
{
IObservable<bool> obsMatch = match.ObservableResult;
var obsMatch = match.Activator;
if (match.ImmediateResult == true)
if (match.Result == SelectorMatchResult.AlwaysThisType ||
match.Result == SelectorMatchResult.AlwaysThisInstance)
{
obsMatch = Observable.Return(true);
}
@ -143,13 +138,15 @@ namespace Avalonia.Styling
foreach (var setter in Setters)
{
var sub = setter.Apply(this, control, match.ObservableResult);
var sub = setter.Apply(this, control, match.Activator);
subs.Add(sub);
}
controlSubscriptions.Add(subs);
Subscriptions.Add(subs);
}
return match.Result != SelectorMatchResult.NeverThisType;
}
else if (control == container)
{
@ -165,7 +162,10 @@ namespace Avalonia.Styling
controlSubscriptions.Add(subs);
Subscriptions.Add(subs);
return true;
}
return false;
}
/// <inheritdoc/>

45
src/Avalonia.Styling/Styling/Styles.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Collections;
using Avalonia.Controls;
@ -15,6 +16,7 @@ namespace Avalonia.Styling
{
private IResourceNode _parent;
private IResourceDictionary _resources;
private Dictionary<Type, List<IStyle>> _cache;
public Styles()
{
@ -34,6 +36,7 @@ namespace Avalonia.Styling
}
x.ResourcesChanged += SubResourceChanged;
_cache = null;
},
x =>
{
@ -49,6 +52,7 @@ namespace Avalonia.Styling
}
x.ResourcesChanged -= SubResourceChanged;
_cache = null;
},
() => { });
}
@ -97,11 +101,46 @@ namespace Avalonia.Styling
/// <param name="container">
/// The control that contains this style. May be null.
/// </param>
public void Attach(IStyleable control, IStyleHost container)
public bool Attach(IStyleable control, IStyleHost container)
{
foreach (IStyle style in this)
if (_cache == null)
{
_cache = new Dictionary<Type, List<IStyle>>();
}
if (_cache.TryGetValue(control.StyleKey, out var cached))
{
if (cached != null)
{
foreach (var style in cached)
{
style.Attach(control, container);
}
return true;
}
return false;
}
else
{
style.Attach(control, container);
List<IStyle> result = null;
foreach (var style in this)
{
if (style.Attach(control, container))
{
if (result == null)
{
result = new List<IStyle>();
}
result.Add(style);
}
}
_cache.Add(control.StyleKey, result);
return result != null;
}
}

5
src/Avalonia.Styling/Styling/TemplateSelector.cs

@ -23,6 +23,9 @@ namespace Avalonia.Styling
/// <inheritdoc/>
public override bool InTemplate => true;
/// <inheritdoc/>
public override bool IsCombinator => true;
/// <inheritdoc/>
public override Type TargetType => null;
@ -46,7 +49,7 @@ namespace Avalonia.Styling
"Cannot call Template selector on control with null TemplatedParent.");
}
return _parent.Match(templatedParent, subscribe) ?? SelectorMatch.True;
return _parent.Match(templatedParent, subscribe);
}
protected override Selector MovePrevious() => null;

19
src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs

@ -68,6 +68,9 @@ namespace Avalonia.Styling
/// <inheritdoc/>
public override Type TargetType => _targetType ?? _previous?.TargetType;
/// <inheritdoc/>
public override bool IsCombinator => false;
/// <summary>
/// Whether the selector matches the concrete <see cref="TargetType"/> or any object which
/// implements <see cref="TargetType"/>.
@ -101,21 +104,21 @@ namespace Avalonia.Styling
{
if (controlType != TargetType)
{
return SelectorMatch.False;
return SelectorMatch.NeverThisType;
}
}
else
{
if (!TargetType.GetTypeInfo().IsAssignableFrom(controlType.GetTypeInfo()))
{
return SelectorMatch.False;
return SelectorMatch.NeverThisType;
}
}
}
if (Name != null && control.Name != Name)
{
return SelectorMatch.False;
return SelectorMatch.NeverThisInstance;
}
if (_classes.IsValueCreated && _classes.Value.Count > 0)
@ -125,15 +128,13 @@ namespace Avalonia.Styling
var observable = new ClassObserver(control.Classes, _classes.Value);
return new SelectorMatch(observable);
}
else
else if (!Matches(control.Classes))
{
return new SelectorMatch(Matches(control.Classes));
return SelectorMatch.NeverThisInstance;
}
}
else
{
return SelectorMatch.True;
}
return Name == null ? SelectorMatch.AlwaysThisType : SelectorMatch.AlwaysThisInstance;
}
protected override Selector MovePrevious() => _previous;

7
src/Avalonia.Visuals/Media/FontFamily.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Media.Fonts;
using Avalonia.Platform;
namespace Avalonia.Media
{
@ -51,6 +52,12 @@ namespace Avalonia.Media
/// </summary>
public static FontFamily Default => new FontFamily(String.Empty);
/// <summary>
/// Represents all font families in the system. This can be an expensive call depending on platform implementation.
/// </summary>
public static IEnumerable<FontFamily> SystemFontFamilies =>
AvaloniaLocator.Current.GetService<IPlatformRenderInterface>().InstalledFontNames.Select(name => new FontFamily(name));
/// <summary>
/// Gets the primary family name of the font family.
/// </summary>

4
src/Avalonia.Visuals/Media/GradientStop.cs

@ -11,13 +11,13 @@ namespace Avalonia.Media
/// <summary>
/// Describes the <see cref="Offset"/> property.
/// </summary>
public static StyledProperty<double> OffsetProperty =
public static readonly StyledProperty<double> OffsetProperty =
AvaloniaProperty.Register<GradientStop, double>(nameof(Offset));
/// <summary>
/// Describes the <see cref="Color"/> property.
/// </summary>
public static StyledProperty<Color> ColorProperty =
public static readonly StyledProperty<Color> ColorProperty =
AvaloniaProperty.Register<GradientStop, Color>(nameof(Color));
/// <summary>

2
src/Avalonia.Visuals/Media/Imaging/Bitmap.cs

@ -75,7 +75,7 @@ namespace Avalonia.Media.Imaging
public Vector Dpi => PlatformImpl.Item.Dpi;
/// <inheritdoc/>
public Size Size => PlatformImpl.Item.PixelSize.ToSize(Dpi);
public Size Size => PlatformImpl.Item.PixelSize.ToSizeWithDpi(Dpi);
/// <inheritdoc/>
public PixelSize PixelSize => PlatformImpl.Item.PixelSize;

201
src/Avalonia.Visuals/Media/PixelPoint.cs

@ -0,0 +1,201 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Globalization;
using Avalonia.Utilities;
namespace Avalonia
{
/// <summary>
/// Represents a point in device pixels.
/// </summary>
public readonly struct PixelPoint
{
/// <summary>
/// A point representing 0,0.
/// </summary>
public static readonly PixelPoint Origin = new PixelPoint(0, 0);
/// <summary>
/// Initializes a new instance of the <see cref="PixelPoint"/> structure.
/// </summary>
/// <param name="x">The X co-ordinate.</param>
/// <param name="y">The Y co-ordinate.</param>
public PixelPoint(int x, int y)
{
X = x;
Y = y;
}
/// <summary>
/// Gets the X co-ordinate.
/// </summary>
public int X { get; }
/// <summary>
/// Gets the Y co-ordinate.
/// </summary>
public int Y { get; }
/// <summary>
/// Checks for equality between two <see cref="PixelPoint"/>s.
/// </summary>
/// <param name="left">The first point.</param>
/// <param name="right">The second point.</param>
/// <returns>True if the points are equal; otherwise false.</returns>
public static bool operator ==(PixelPoint left, PixelPoint right)
{
return left.X == right.X && left.Y == right.Y;
}
/// <summary>
/// Checks for inequality between two <see cref="PixelPoint"/>s.
/// </summary>
/// <param name="left">The first point.</param>
/// <param name="right">The second point.</param>
/// <returns>True if the points are unequal; otherwise false.</returns>
public static bool operator !=(PixelPoint left, PixelPoint right)
{
return !(left == right);
}
/// <summary>
/// Parses a <see cref="PixelPoint"/> string.
/// </summary>
/// <param name="s">The string.</param>
/// <returns>The <see cref="PixelPoint"/>.</returns>
public static PixelPoint Parse(string s)
{
using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid PixelPoint"))
{
return new PixelPoint(
tokenizer.ReadInt32(),
tokenizer.ReadInt32());
}
}
/// <summary>
/// Checks for equality between a point and an object.
/// </summary>
/// <param name="obj">The object.</param>
/// <returns>
/// True if <paramref name="obj"/> is a point that equals the current point.
/// </returns>
public override bool Equals(object obj)
{
if (obj is PixelPoint other)
{
return this == other;
}
return false;
}
/// <summary>
/// Returns a hash code for a <see cref="PixelPoint"/>.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = (hash * 23) + X.GetHashCode();
hash = (hash * 23) + Y.GetHashCode();
return hash;
}
}
/// <summary>
/// Returns a new <see cref="PixelPoint"/> with the same Y co-ordinate and the specified X co-ordinate.
/// </summary>
/// <param name="x">The X co-ordinate.</param>
/// <returns>The new <see cref="PixelPoint"/>.</returns>
public PixelPoint WithX(int x) => new PixelPoint(x, Y);
/// <summary>
/// Returns a new <see cref="PixelPoint"/> with the same X co-ordinate and the specified Y co-ordinate.
/// </summary>
/// <param name="y">The Y co-ordinate.</param>
/// <returns>The new <see cref="PixelPoint"/>.</returns>
public PixelPoint WithY(int y) => new PixelPoint(X, y);
/// <summary>
/// Converts the <see cref="PixelPoint"/> to a device-independent <see cref="Point"/> using the
/// specified scaling factor.
/// </summary>
/// <param name="scale">The scaling factor.</param>
/// <returns>The device-independent point.</returns>
public Point ToPoint(double scale) => new Point(X / scale, Y / scale);
/// <summary>
/// Converts the <see cref="PixelPoint"/> to a device-independent <see cref="Point"/> using the
/// specified scaling factor.
/// </summary>
/// <param name="scale">The scaling factor.</param>
/// <returns>The device-independent point.</returns>
public Point ToPoint(Vector scale) => new Point(X / scale.X, Y / scale.Y);
/// <summary>
/// Converts the <see cref="PixelPoint"/> to a device-independent <see cref="Point"/> using the
/// specified dots per inch (DPI).
/// </summary>
/// <param name="dpi">The dots per inch of the device.</param>
/// <returns>The device-independent point.</returns>
public Point ToPointWithDpi(double dpi) => ToPoint(dpi / 96);
/// <summary>
/// Converts the <see cref="PixelPoint"/> to a device-independent <see cref="Point"/> using the
/// specified dots per inch (DPI).
/// </summary>
/// <param name="dpi">The dots per inch of the device.</param>
/// <returns>The device-independent point.</returns>
public Point ToPointWithDpi(Vector dpi) => ToPoint(new Vector(dpi.X / 96, dpi.Y / 96));
/// <summary>
/// Converts a <see cref="Point"/> to device pixels using the specified scaling factor.
/// </summary>
/// <param name="point">The point.</param>
/// <param name="scale">The scaling factor.</param>
/// <returns>The device-independent point.</returns>
public static PixelPoint FromPoint(Point point, double scale) => new PixelPoint(
(int)(point.X * scale),
(int)(point.Y * scale));
/// <summary>
/// Converts a <see cref="Point"/> to device pixels using the specified scaling factor.
/// </summary>
/// <param name="point">The point.</param>
/// <param name="scale">The scaling factor.</param>
/// <returns>The device-independent point.</returns>
public static PixelPoint FromPoint(Point point, Vector scale) => new PixelPoint(
(int)(point.X * scale.X),
(int)(point.Y * scale.Y));
/// <summary>
/// Converts a <see cref="Point"/> to device pixels using the specified dots per inch (DPI).
/// </summary>
/// <param name="point">The point.</param>
/// <param name="dpi">The dots per inch of the device.</param>
/// <returns>The device-independent point.</returns>
public static PixelPoint FromPointWithDpi(Point point, double dpi) => FromPoint(point, dpi / 96);
/// <summary>
/// Converts a <see cref="Point"/> to device pixels using the specified dots per inch (DPI).
/// </summary>
/// <param name="point">The point.</param>
/// <param name="dpi">The dots per inch of the device.</param>
/// <returns>The device-independent point.</returns>
public static PixelPoint FromPointWithDpi(Point point, Vector dpi) => FromPoint(point, new Vector(dpi.X / 96, dpi.Y / 96));
/// <summary>
/// Returns the string representation of the point.
/// </summary>
/// <returns>The string representation of the point.</returns>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "{0}, {1}", X, Y);
}
}
}

436
src/Avalonia.Visuals/Media/PixelRect.cs

@ -0,0 +1,436 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Globalization;
using Avalonia.Utilities;
namespace Avalonia
{
/// <summary>
/// Represents a rectangle in device pixels.
/// </summary>
public readonly struct PixelRect
{
/// <summary>
/// An empty rectangle.
/// </summary>
public static readonly PixelRect Empty = default;
/// <summary>
/// Initializes a new instance of the <see cref="PixelRect"/> structure.
/// </summary>
/// <param name="x">The X position.</param>
/// <param name="y">The Y position.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
public PixelRect(int x, int y, int width, int height)
{
X = x;
Y = y;
Width = width;
Height = height;
}
/// <summary>
/// Initializes a new instance of the <see cref="PixelRect"/> structure.
/// </summary>
/// <param name="size">The size of the rectangle.</param>
public PixelRect(PixelSize size)
{
X = 0;
Y = 0;
Width = size.Width;
Height = size.Height;
}
/// <summary>
/// Initializes a new instance of the <see cref="PixelRect"/> structure.
/// </summary>
/// <param name="position">The position of the rectangle.</param>
/// <param name="size">The size of the rectangle.</param>
public PixelRect(PixelPoint position, PixelSize size)
{
X = position.X;
Y = position.Y;
Width = size.Width;
Height = size.Height;
}
/// <summary>
/// Initializes a new instance of the <see cref="PixelRect"/> structure.
/// </summary>
/// <param name="topLeft">The top left position of the rectangle.</param>
/// <param name="bottomRight">The bottom right position of the rectangle.</param>
public PixelRect(PixelPoint topLeft, PixelPoint bottomRight)
{
X = topLeft.X;
Y = topLeft.Y;
Width = bottomRight.X - topLeft.X;
Height = bottomRight.Y - topLeft.Y;
}
/// <summary>
/// Gets the X position.
/// </summary>
public int X { get; }
/// <summary>
/// Gets the Y position.
/// </summary>
public int Y { get; }
/// <summary>
/// Gets the width.
/// </summary>
public int Width { get; }
/// <summary>
/// Gets the height.
/// </summary>
public int Height { get; }
/// <summary>
/// Gets the position of the rectangle.
/// </summary>
public PixelPoint Position => new PixelPoint(X, Y);
/// <summary>
/// Gets the size of the rectangle.
/// </summary>
public PixelSize Size => new PixelSize(Width, Height);
/// <summary>
/// Gets the right position of the rectangle.
/// </summary>
public int Right => X + Width;
/// <summary>
/// Gets the bottom position of the rectangle.
/// </summary>
public int Bottom => Y + Height;
/// <summary>
/// Gets the top left point of the rectangle.
/// </summary>
public PixelPoint TopLeft => new PixelPoint(X, Y);
/// <summary>
/// Gets the top right point of the rectangle.
/// </summary>
public PixelPoint TopRight => new PixelPoint(Right, Y);
/// <summary>
/// Gets the bottom left point of the rectangle.
/// </summary>
public PixelPoint BottomLeft => new PixelPoint(X, Bottom);
/// <summary>
/// Gets the bottom right point of the rectangle.
/// </summary>
public PixelPoint BottomRight => new PixelPoint(Right, Bottom);
/// <summary>
/// Gets the center point of the rectangle.
/// </summary>
public PixelPoint Center => new PixelPoint(X + (Width / 2), Y + (Height / 2));
/// <summary>
/// Gets a value that indicates whether the rectangle is empty.
/// </summary>
public bool IsEmpty => Width == 0 && Height == 0;
/// <summary>
/// Checks for equality between two <see cref="PixelRect"/>s.
/// </summary>
/// <param name="left">The first rect.</param>
/// <param name="right">The second rect.</param>
/// <returns>True if the rects are equal; otherwise false.</returns>
public static bool operator ==(PixelRect left, PixelRect right)
{
return left.Position == right.Position && left.Size == right.Size;
}
/// <summary>
/// Checks for inequality between two <see cref="PixelRect"/>s.
/// </summary>
/// <param name="left">The first rect.</param>
/// <param name="right">The second rect.</param>
/// <returns>True if the rects are unequal; otherwise false.</returns>
public static bool operator !=(PixelRect left, PixelRect right)
{
return !(left == right);
}
/// <summary>
/// Determines whether a point in in the bounds of the rectangle.
/// </summary>
/// <param name="p">The point.</param>
/// <returns>true if the point is in the bounds of the rectangle; otherwise false.</returns>
public bool Contains(PixelPoint p)
{
return p.X >= X && p.X <= Right && p.Y >= Y && p.Y <= Bottom;
}
/// <summary>
/// Determines whether the rectangle fully contains another rectangle.
/// </summary>
/// <param name="r">The rectangle.</param>
/// <returns>true if the rectangle is fully contained; otherwise false.</returns>
public bool Contains(PixelRect r)
{
return Contains(r.TopLeft) && Contains(r.BottomRight);
}
/// <summary>
/// Centers another rectangle in this rectangle.
/// </summary>
/// <param name="rect">The rectangle to center.</param>
/// <returns>The centered rectangle.</returns>
public PixelRect CenterRect(PixelRect rect)
{
return new PixelRect(
X + ((Width - rect.Width) / 2),
Y + ((Height - rect.Height) / 2),
rect.Width,
rect.Height);
}
/// <summary>
/// Returns a boolean indicating whether the given object is equal to this rectangle.
/// </summary>
/// <param name="obj">The object to compare against.</param>
/// <returns>True if the object is equal to this rectangle; false otherwise.</returns>
public override bool Equals(object obj)
{
if (obj is PixelRect other)
{
return this == other;
}
return false;
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = (hash * 23) + X.GetHashCode();
hash = (hash * 23) + Y.GetHashCode();
hash = (hash * 23) + Width.GetHashCode();
hash = (hash * 23) + Height.GetHashCode();
return hash;
}
}
/// <summary>
/// Gets the intersection of two rectangles.
/// </summary>
/// <param name="rect">The other rectangle.</param>
/// <returns>The intersection.</returns>
public PixelRect Intersect(PixelRect rect)
{
var newLeft = (rect.X > X) ? rect.X : X;
var newTop = (rect.Y > Y) ? rect.Y : Y;
var newRight = (rect.Right < Right) ? rect.Right : Right;
var newBottom = (rect.Bottom < Bottom) ? rect.Bottom : Bottom;
if ((newRight > newLeft) && (newBottom > newTop))
{
return new PixelRect(newLeft, newTop, newRight - newLeft, newBottom - newTop);
}
else
{
return Empty;
}
}
/// <summary>
/// Determines whether a rectangle intersects with this rectangle.
/// </summary>
/// <param name="rect">The other rectangle.</param>
/// <returns>
/// True if the specified rectangle intersects with this one; otherwise false.
/// </returns>
public bool Intersects(PixelRect rect)
{
return (rect.X < Right) && (X < rect.Right) && (rect.Y < Bottom) && (Y < rect.Bottom);
}
/// <summary>
/// Gets the union of two rectangles.
/// </summary>
/// <param name="rect">The other rectangle.</param>
/// <returns>The union.</returns>
public PixelRect Union(PixelRect rect)
{
if (IsEmpty)
{
return rect;
}
else if (rect.IsEmpty)
{
return this;
}
else
{
var x1 = Math.Min(X, rect.X);
var x2 = Math.Max(Right, rect.Right);
var y1 = Math.Min(Y, rect.Y);
var y2 = Math.Max(Bottom, rect.Bottom);
return new PixelRect(new PixelPoint(x1, y1), new PixelPoint(x2, y2));
}
}
/// <summary>
/// Returns a new <see cref="PixelRect"/> with the specified X position.
/// </summary>
/// <param name="x">The x position.</param>
/// <returns>The new <see cref="PixelRect"/>.</returns>
public PixelRect WithX(int x)
{
return new PixelRect(x, Y, Width, Height);
}
/// <summary>
/// Returns a new <see cref="PixelRect"/> with the specified Y position.
/// </summary>
/// <param name="y">The y position.</param>
/// <returns>The new <see cref="PixelRect"/>.</returns>
public PixelRect WithY(int y)
{
return new PixelRect(X, y, Width, Height);
}
/// <summary>
/// Returns a new <see cref="PixelRect"/> with the specified width.
/// </summary>
/// <param name="width">The width.</param>
/// <returns>The new <see cref="PixelRect"/>.</returns>
public PixelRect WithWidth(int width)
{
return new PixelRect(X, Y, width, Height);
}
/// <summary>
/// Returns a new <see cref="PixelRect"/> with the specified height.
/// </summary>
/// <param name="height">The height.</param>
/// <returns>The new <see cref="PixelRect"/>.</returns>
public PixelRect WithHeight(int height)
{
return new PixelRect(X, Y, Width, Height);
}
/// <summary>
/// Converts the <see cref="PixelRect"/> to a device-independent <see cref="Rect"/> using the
/// specified scaling factor.
/// </summary>
/// <param name="scale">The scaling factor.</param>
/// <returns>The device-independent rect.</returns>
public Rect ToRect(double scale) => new Rect(Position.ToPoint(scale), Size.ToSize(scale));
/// <summary>
/// Converts the <see cref="PixelRect"/> to a device-independent <see cref="Rect"/> using the
/// specified scaling factor.
/// </summary>
/// <param name="scale">The scaling factor.</param>
/// <returns>The device-independent rect.</returns>
public Rect ToRect(Vector scale) => new Rect(Position.ToPoint(scale), Size.ToSize(scale));
/// <summary>
/// Converts the <see cref="PixelRect"/> to a device-independent <see cref="Rect"/> using the
/// specified dots per inch (DPI).
/// </summary>
/// <param name="dpi">The dots per inch of the device.</param>
/// <returns>The device-independent rect.</returns>
public Rect ToRectWithDpi(double dpi) => new Rect(Position.ToPointWithDpi(dpi), Size.ToSizeWithDpi(dpi));
/// <summary>
/// Converts the <see cref="PixelRect"/> to a device-independent <see cref="Rect"/> using the
/// specified dots per inch (DPI).
/// </summary>
/// <param name="dpi">The dots per inch of the device.</param>
/// <returns>The device-independent rect.</returns>
public Rect ToRectWithDpi(Vector dpi) => new Rect(Position.ToPointWithDpi(dpi), Size.ToSizeWithDpi(dpi));
/// <summary>
/// Converts a <see cref="Rect"/> to device pixels using the specified scaling factor.
/// </summary>
/// <param name="rect">The rect.</param>
/// <param name="scale">The scaling factor.</param>
/// <returns>The device-independent rect.</returns>
public static PixelRect FromRect(Rect rect, double scale) => new PixelRect(
PixelPoint.FromPoint(rect.Position, scale),
PixelSize.FromSize(rect.Size, scale));
/// <summary>
/// Converts a <see cref="Rect"/> to device pixels using the specified scaling factor.
/// </summary>
/// <param name="rect">The rect.</param>
/// <param name="scale">The scaling factor.</param>
/// <returns>The device-independent point.</returns>
public static PixelRect FromRect(Rect rect, Vector scale) => new PixelRect(
PixelPoint.FromPoint(rect.Position, scale),
PixelSize.FromSize(rect.Size, scale));
/// <summary>
/// Converts a <see cref="Rect"/> to device pixels using the specified dots per inch (DPI).
/// </summary>
/// <param name="rect">The rect.</param>
/// <param name="dpi">The dots per inch of the device.</param>
/// <returns>The device-independent point.</returns>
public static PixelRect FromRectWithDpi(Rect rect, double dpi) => new PixelRect(
PixelPoint.FromPointWithDpi(rect.Position, dpi),
PixelSize.FromSizeWithDpi(rect.Size, dpi));
/// <summary>
/// Converts a <see cref="Rect"/> to device pixels using the specified dots per inch (DPI).
/// </summary>
/// <param name="rect">The rect.</param>
/// <param name="dpi">The dots per inch of the device.</param>
/// <returns>The device-independent point.</returns>
public static PixelRect FromRectWithDpi(Rect rect, Vector dpi) => new PixelRect(
PixelPoint.FromPointWithDpi(rect.Position, dpi),
PixelSize.FromSizeWithDpi(rect.Size, dpi));
/// <summary>
/// Returns the string representation of the rectangle.
/// </summary>
/// <returns>The string representation of the rectangle.</returns>
public override string ToString()
{
return string.Format(
CultureInfo.InvariantCulture,
"{0}, {1}, {2}, {3}",
X,
Y,
Width,
Height);
}
/// <summary>
/// Parses a <see cref="PixelRect"/> string.
/// </summary>
/// <param name="s">The string.</param>
/// <returns>The parsed <see cref="PixelRect"/>.</returns>
public static PixelRect Parse(string s)
{
using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid PixelRect"))
{
return new PixelRect(
tokenizer.ReadInt32(),
tokenizer.ReadInt32(),
tokenizer.ReadInt32(),
tokenizer.ReadInt32()
);
}
}
}
}

50
src/Avalonia.Visuals/Media/PixelSize.cs

@ -72,7 +72,7 @@ namespace Avalonia
/// <returns>The <see cref="PixelSize"/>.</returns>
public static PixelSize Parse(string s)
{
using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Size"))
using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid PixelSize"))
{
return new PixelSize(
tokenizer.ReadInt32(),
@ -126,13 +126,29 @@ namespace Avalonia
/// <returns>The new <see cref="PixelSize"/>.</returns>
public PixelSize WithHeight(int height) => new PixelSize(Width, height);
/// <summary>
/// Converts the <see cref="PixelSize"/> to a device-independent <see cref="Size"/> using the
/// specified scaling factor.
/// </summary>
/// <param name="scale">The scaling factor.</param>
/// <returns>The device-independent size.</returns>
public Size ToSize(double scale) => new Size(Width / scale, Height / scale);
/// <summary>
/// Converts the <see cref="PixelSize"/> to a device-independent <see cref="Size"/> using the
/// specified scaling factor.
/// </summary>
/// <param name="scale">The scaling factor.</param>
/// <returns>The device-independent size.</returns>
public Size ToSize(Vector scale) => new Size(Width / scale.X, Height / scale.Y);
/// <summary>
/// Converts the <see cref="PixelSize"/> to a device-independent <see cref="Size"/> using the
/// specified dots per inch (DPI).
/// </summary>
/// <param name="dpi">The dots per inch.</param>
/// <returns>The device-independent size.</returns>
public Size ToSize(double dpi) => new Size(Width / (dpi / 96), Height / (dpi / 96));
public Size ToSizeWithDpi(double dpi) => ToSize(dpi / 96);
/// <summary>
/// Converts the <see cref="PixelSize"/> to a device-independent <see cref="Size"/> using the
@ -140,7 +156,27 @@ namespace Avalonia
/// </summary>
/// <param name="dpi">The dots per inch.</param>
/// <returns>The device-independent size.</returns>
public Size ToSize(Vector dpi) => new Size(Width / (dpi.X / 96), Height / (dpi.Y / 96));
public Size ToSizeWithDpi(Vector dpi) => ToSize(new Vector(dpi.X / 96, dpi.Y / 96));
/// <summary>
/// Converts a <see cref="Size"/> to device pixels using the specified scaling factor.
/// </summary>
/// <param name="size">The size.</param>
/// <param name="scale">The scaling factor.</param>
/// <returns>The device-independent size.</returns>
public static PixelSize FromSize(Size size, double scale) => new PixelSize(
(int)Math.Ceiling(size.Width * scale),
(int)Math.Ceiling(size.Height * scale));
/// <summary>
/// Converts a <see cref="Size"/> to device pixels using the specified scaling factor.
/// </summary>
/// <param name="size">The size.</param>
/// <param name="scale">The scaling factor.</param>
/// <returns>The device-independent size.</returns>
public static PixelSize FromSize(Size size, Vector scale) => new PixelSize(
(int)Math.Ceiling(size.Width * scale.X),
(int)Math.Ceiling(size.Height * scale.Y));
/// <summary>
/// Converts a <see cref="Size"/> to device pixels using the specified dots per inch (DPI).
@ -148,9 +184,7 @@ namespace Avalonia
/// <param name="size">The size.</param>
/// <param name="dpi">The dots per inch.</param>
/// <returns>The device-independent size.</returns>
public static PixelSize FromSize(Size size, double dpi) => new PixelSize(
(int)(size.Width * (dpi / 96)),
(int)(size.Height * (dpi / 96)));
public static PixelSize FromSizeWithDpi(Size size, double dpi) => FromSize(size, dpi / 96);
/// <summary>
/// Converts a <see cref="Size"/> to device pixels using the specified dots per inch (DPI).
@ -158,9 +192,7 @@ namespace Avalonia
/// <param name="size">The size.</param>
/// <param name="dpi">The dots per inch.</param>
/// <returns>The device-independent size.</returns>
public static PixelSize FromSize(Size size, Vector dpi) => new PixelSize(
(int)Math.Ceiling(size.Width * (dpi.X / 96)),
(int)Math.Ceiling(size.Height * (dpi.Y / 96)));
public static PixelSize FromSizeWithDpi(Size size, Vector dpi) => FromSize(size, new Vector(dpi.X / 96, dpi.Y / 96));
/// <summary>
/// Returns the string representation of the size.

2
src/Avalonia.Visuals/Media/Typeface.cs

@ -7,7 +7,7 @@ namespace Avalonia.Media
/// </summary>
public class Typeface
{
public static Typeface Default = new Typeface(FontFamily.Default);
public static readonly Typeface Default = new Typeface(FontFamily.Default);
/// <summary>
/// Initializes a new instance of the <see cref="Typeface"/> class.

5
src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs

@ -13,6 +13,11 @@ namespace Avalonia.Platform
/// </summary>
public interface IPlatformRenderInterface
{
/// <summary>
/// Get all installed fonts in the system
/// </summary>
IEnumerable<string> InstalledFontNames { get; }
/// <summary>
/// Creates a formatted text implementation.
/// </summary>

56
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -128,9 +128,25 @@ namespace Avalonia.Rendering
}
Stop();
DisposeRenderTarget();
}
void DisposeRenderTarget()
{
using (var l = _lock.TryLock())
{
if(l == null)
{
// We are still trying to render on the render thread, try again a bit later
DispatcherTimer.RunOnce(DisposeRenderTarget, TimeSpan.FromMilliseconds(50),
DispatcherPriority.Background);
return;
}
Layers.Clear();
RenderTarget?.Dispose();
Layers.Clear();
RenderTarget?.Dispose();
RenderTarget = null;
}
}
/// <inheritdoc/>
@ -152,7 +168,8 @@ namespace Avalonia.Rendering
var t = (IRenderLoopTask)this;
if(t.NeedsUpdate)
UpdateScene();
Render(true);
if(_scene.Item != null)
Render(true);
}
/// <inheritdoc/>
@ -336,16 +353,34 @@ namespace Avalonia.Rendering
private void RenderToLayers(Scene scene)
{
if (scene.Layers.HasDirty)
foreach (var layer in scene.Layers)
{
foreach (var layer in scene.Layers)
{
var renderTarget = Layers[layer.LayerRoot].Bitmap;
var node = (VisualNode)scene.FindNode(layer.LayerRoot);
var renderLayer = Layers[layer.LayerRoot];
if (layer.Dirty.IsEmpty && !renderLayer.IsEmpty)
continue;
var renderTarget = renderLayer.Bitmap;
var node = (VisualNode)scene.FindNode(layer.LayerRoot);
if (node != null)
if (node != null)
{
using (var context = renderTarget.Item.CreateDrawingContext(this))
{
using (var context = renderTarget.Item.CreateDrawingContext(this))
if (renderLayer.IsEmpty)
{
// Render entire layer root node
context.Clear(Colors.Transparent);
context.Transform = Matrix.Identity;
context.PushClip(node.ClipBounds);
Render(context, node, layer.LayerRoot, node.ClipBounds);
context.PopClip();
if (DrawDirtyRects)
{
_dirtyRectsDisplay.Add(node.ClipBounds);
}
renderLayer.IsEmpty = false;
}
else
{
foreach (var rect in layer.Dirty)
{
@ -364,6 +399,7 @@ namespace Avalonia.Rendering
}
}
}
}
private void RenderOverlay(Scene scene, IDrawingContextImpl parentContent)

8
src/Avalonia.Visuals/Rendering/IRenderRoot.cs

@ -41,15 +41,15 @@ namespace Avalonia.Rendering
/// <summary>
/// Converts a point from screen to client coordinates.
/// </summary>
/// <param name="point">The point in screen coordinates.</param>
/// <param name="point">The point in screen device coordinates.</param>
/// <returns>The point in client coordinates.</returns>
Point PointToClient(Point point);
Point PointToClient(PixelPoint point);
/// <summary>
/// Converts a point from client to screen coordinates.
/// </summary>
/// <param name="point">The point in client coordinates.</param>
/// <returns>The point in screen coordinates.</returns>
Point PointToScreen(Point point);
/// <returns>The point in screen device coordinates.</returns>
PixelPoint PointToScreen(Point point);
}
}

12
src/Avalonia.Visuals/Rendering/RenderLayer.cs

@ -7,39 +7,39 @@ namespace Avalonia.Rendering
{
public class RenderLayer
{
private readonly IDrawingContextImpl _drawingContext;
public RenderLayer(
IDrawingContextImpl drawingContext,
Size size,
double scaling,
IVisual layerRoot)
{
_drawingContext = drawingContext;
Bitmap = RefCountable.Create(drawingContext.CreateLayer(size));
Size = size;
Scaling = scaling;
LayerRoot = layerRoot;
IsEmpty = true;
}
public IRef<IRenderTargetBitmapImpl> Bitmap { get; private set; }
public bool IsEmpty { get; set; }
public double Scaling { get; private set; }
public Size Size { get; private set; }
public IVisual LayerRoot { get; }
public void ResizeBitmap(Size size, double scaling)
public void RecreateBitmap(IDrawingContextImpl drawingContext, Size size, double scaling)
{
if (Size != size || Scaling != scaling)
{
var resized = RefCountable.Create(_drawingContext.CreateLayer(size));
var resized = RefCountable.Create(drawingContext.CreateLayer(size));
using (var context = resized.Item.CreateDrawingContext(null))
{
context.Clear(Colors.Transparent);
context.DrawImage(Bitmap, 1, new Rect(Size), new Rect(Size));
Bitmap.Dispose();
Bitmap = resized;
Scaling = scaling;
Size = size;
IsEmpty = true;
}
}
}

6
src/Avalonia.Visuals/Rendering/RenderLayers.cs

@ -29,11 +29,11 @@ namespace Avalonia.Rendering
}
else
{
layer.ResizeBitmap(scene.Size, scene.Scaling);
layer.RecreateBitmap(context, scene.Size, scene.Scaling);
}
}
for (var i = _inner.Count - 1; i >= 0; --i)
for (var i = 0; i < _inner.Count;)
{
var layer = _inner[i];
@ -43,6 +43,8 @@ namespace Avalonia.Rendering
_inner.RemoveAt(i);
_index.Remove(layer.LayerRoot);
}
else
i++;
}
}

10
src/Avalonia.Visuals/VisualExtensions.cs

@ -18,10 +18,12 @@ namespace Avalonia
/// <param name="visual">The visual.</param>
/// <param name="point">The point in screen coordinates.</param>
/// <returns>The point in client coordinates.</returns>
public static Point PointToClient(this IVisual visual, Point point)
public static Point PointToClient(this IVisual visual, PixelPoint point)
{
var p = GetRootAndPosition(visual);
return p.Item1.PointToClient(point - p.Item2);
var (root, offset) = GetRootAndPosition(visual);
var screenOffset = PixelPoint.FromPoint((Point)offset, root.RenderScaling);
var screenPoint = new PixelPoint(point.X - screenOffset.X, point.Y - screenOffset.Y);
return root.PointToClient(screenPoint);
}
/// <summary>
@ -30,7 +32,7 @@ namespace Avalonia
/// <param name="visual">The visual.</param>
/// <param name="point">The point in client coordinates.</param>
/// <returns>The point in screen coordinates.</returns>
public static Point PointToScreen(this IVisual visual, Point point)
public static PixelPoint PointToScreen(this IVisual visual, Point point)
{
var p = GetRootAndPosition(visual);
return p.Item1.PointToScreen(point + p.Item2);

14
src/Avalonia.X11/Avalonia.X11.csproj

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\Gtk\Avalonia.Gtk3\Avalonia.Gtk3.csproj" />
<ProjectReference Include="..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
</ItemGroup>
</Project>

2108
src/Avalonia.X11/Keysyms.cs

File diff suppressed because it is too large

17
src/Avalonia.X11/Stubs.cs

@ -0,0 +1,17 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Platform;
namespace Avalonia.X11
{
class PlatformSettingsStub : IPlatformSettings
{
public Size DoubleClickSize { get; } = new Size(2, 2);
public TimeSpan DoubleClickTime { get; } = TimeSpan.FromMilliseconds(500);
}
}

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

Loading…
Cancel
Save