Browse Source

Merge branch 'master' into detachable-styles

pull/1887/head
Steven Kirk 8 years ago
committed by GitHub
parent
commit
2de646ddf8
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 26
      Avalonia.sln
  2. 1
      build.cake
  3. 2
      build/ReactiveUI.props
  4. 1
      packages.cake
  5. 9
      samples/ControlCatalog/App.xaml
  6. 61
      samples/ControlCatalog/Pages/MenuPage.xaml
  7. 97
      samples/ControlCatalog/Pages/MenuPage.xaml.cs
  8. 7
      samples/RenderDemo/Pages/AnimationsPage.xaml
  9. 16
      samples/RenderDemo/Pages/AnimationsPage.xaml.cs
  10. 26
      samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs
  11. 1
      samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs
  12. 278
      samples/interop/Direct3DInteropSample/MainWindow.cs
  13. 3
      src/Android/Avalonia.Android/AndroidPlatform.cs
  14. 28
      src/Avalonia.Animation/Animatable.cs
  15. 15
      src/Avalonia.Animation/Animation.cs
  16. 61
      src/Avalonia.Animation/AnimationInstance`1.cs
  17. 118
      src/Avalonia.Animation/Animator`1.cs
  18. 30
      src/Avalonia.Animation/Clock.cs
  19. 72
      src/Avalonia.Animation/ClockBase.cs
  20. 65
      src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs
  21. 5
      src/Avalonia.Animation/DoubleAnimator.cs
  22. 5
      src/Avalonia.Animation/FillMode.cs
  23. 11
      src/Avalonia.Animation/IAnimation.cs
  24. 3
      src/Avalonia.Animation/IAnimationSetter.cs
  25. 7
      src/Avalonia.Animation/IAnimator.cs
  26. 11
      src/Avalonia.Animation/IClock.cs
  27. 10
      src/Avalonia.Animation/IGlobalClock.cs
  28. 2
      src/Avalonia.Animation/ITransition.cs
  29. 5
      src/Avalonia.Animation/KeyFrame.cs
  30. 3
      src/Avalonia.Animation/KeyFramePair`1.cs
  31. 5
      src/Avalonia.Animation/PlayState.cs
  32. 5
      src/Avalonia.Animation/PlaybackDirection.cs
  33. 54
      src/Avalonia.Animation/Timing.cs
  34. 27
      src/Avalonia.Animation/TransitionInstance.cs
  35. 7
      src/Avalonia.Animation/Transition`1.cs
  36. 1
      src/Avalonia.Base/Avalonia.Base.csproj
  37. 18
      src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs
  38. 4
      src/Avalonia.Base/Platform/Interop/Utf8Buffer.cs
  39. 7
      src/Avalonia.Base/PriorityBindingEntry.cs
  40. 14
      src/Avalonia.Base/PriorityLevel.cs
  41. 10
      src/Avalonia.Base/Reactive/LightweightObservableBase.cs
  42. 5
      src/Avalonia.Base/Reactive/ObservableEx.cs
  43. 2
      src/Avalonia.Controls/AppBuilderBase.cs
  44. 7
      src/Avalonia.Controls/Application.cs
  45. 6
      src/Avalonia.Controls/Border.cs
  46. 2
      src/Avalonia.Controls/DrawingPresenter.cs
  47. 4
      src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
  48. 1
      src/Avalonia.Controls/Image.cs
  49. 38
      src/Avalonia.Controls/MenuItem.cs
  50. 1
      src/Avalonia.Controls/Panel.cs
  51. 2
      src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
  52. 11
      src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs
  53. 8
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  54. 8
      src/Avalonia.Controls/Primitives/AdornerLayer.cs
  55. 8
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  56. 7
      src/Avalonia.Controls/ProgressBar.cs
  57. 4
      src/Avalonia.Controls/TextBlock.cs
  58. 5
      src/Avalonia.Controls/TextBox.cs
  59. 1
      src/Avalonia.Controls/TopLevel.cs
  60. 156
      src/Avalonia.Controls/Utils/BorderRenderHelper.cs
  61. 3
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs
  62. 8
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  63. 1
      src/Avalonia.Diagnostics/DevTools.xaml
  64. 23
      src/Avalonia.Diagnostics/DevTools.xaml.cs
  65. 38
      src/Avalonia.Diagnostics/Models/EventChainLink.cs
  66. 5
      src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs
  67. 61
      src/Avalonia.Diagnostics/ViewModels/EventOwnerTreeNode.cs
  68. 98
      src/Avalonia.Diagnostics/ViewModels/EventTreeNode.cs
  69. 78
      src/Avalonia.Diagnostics/ViewModels/EventTreeNodeBase.cs
  70. 60
      src/Avalonia.Diagnostics/ViewModels/EventsViewModel.cs
  71. 80
      src/Avalonia.Diagnostics/ViewModels/FiredEvent.cs
  72. 53
      src/Avalonia.Diagnostics/Views/EventsView.xaml
  73. 32
      src/Avalonia.Diagnostics/Views/EventsView.xaml.cs
  74. 12
      src/Avalonia.OpenGL/Avalonia.OpenGL.csproj
  75. 196
      src/Avalonia.OpenGL/EglConsts.cs
  76. 205
      src/Avalonia.OpenGL/EglDisplay.cs
  77. 39
      src/Avalonia.OpenGL/EglGlPlatformFeature.cs
  78. 80
      src/Avalonia.OpenGL/EglGlPlatformSurface.cs
  79. 93
      src/Avalonia.OpenGL/EglInterface.cs
  80. 16
      src/Avalonia.OpenGL/EntryPointAttribute.cs
  81. 789
      src/Avalonia.OpenGL/GlConsts.cs
  82. 8
      src/Avalonia.OpenGL/GlDisplayType.cs
  83. 48
      src/Avalonia.OpenGL/GlInterface.cs
  84. 28
      src/Avalonia.OpenGL/GlInterfaceBase.cs
  85. 8
      src/Avalonia.OpenGL/IGlContext.cs
  86. 11
      src/Avalonia.OpenGL/IGlDisplay.cs
  87. 7
      src/Avalonia.OpenGL/IGlPlatformSurface.cs
  88. 9
      src/Avalonia.OpenGL/IGlPlatformSurfaceRenderTarget.cs
  89. 12
      src/Avalonia.OpenGL/IGlPlatformSurfaceRenderingSession.cs
  90. 10
      src/Avalonia.OpenGL/IGlSurface.cs
  91. 7
      src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs
  92. 12
      src/Avalonia.OpenGL/OpenGlException.cs
  93. 4
      src/Avalonia.ReactiveUI/AppBuilderExtensions.cs
  94. 38
      src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs
  95. 19
      src/Avalonia.Styling/Styling/Style.cs
  96. 12
      src/Avalonia.Themes.Default/MenuItem.xaml
  97. 26
      src/Avalonia.Visuals/Animation/RenderLoopClock.cs
  98. 20
      src/Avalonia.Visuals/Animation/TransformAnimator.cs
  99. 1
      src/Avalonia.Visuals/Avalonia.Visuals.csproj
  100. 2
      src/Avalonia.Visuals/Matrix.cs

26
Avalonia.sln

@ -186,6 +186,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Designer.HostApp.N
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.UnitTests", "tests\Avalonia.Skia.UnitTests\Avalonia.Skia.UnitTests.csproj", "{E1240B49-7B4B-4371-A00E-068778C5CF0B}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.UnitTests", "tests\Avalonia.Skia.UnitTests\Avalonia.Skia.UnitTests.csproj", "{E1240B49-7B4B-4371-A00E-068778C5CF0B}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.OpenGL", "src\Avalonia.OpenGL\Avalonia.OpenGL.csproj", "{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}"
EndProject
Global Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13 src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@ -1663,6 +1665,30 @@ Global
{E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|iPhone.Build.0 = Release|Any CPU {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|iPhone.Build.0 = Release|Any CPU
{E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|iPhone.Build.0 = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|iPhone.Build.0 = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|Any CPU.Build.0 = Release|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhone.ActiveCfg = Release|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhone.Build.0 = Release|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

1
build.cake

@ -170,6 +170,7 @@ Task("Run-Unit-Tests-Impl")
RunCoreTest("./tests/Avalonia.Styling.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Styling.UnitTests", data.Parameters, false);
RunCoreTest("./tests/Avalonia.Visuals.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Visuals.UnitTests", data.Parameters, false);
RunCoreTest("./tests/Avalonia.Skia.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Skia.UnitTests", data.Parameters, false);
RunCoreTest("./tests/Avalonia.ReactiveUI.UnitTests", data.Parameters, false);
if (data.Parameters.IsRunningOnWindows) if (data.Parameters.IsRunningOnWindows)
{ {
RunCoreTest("./tests/Avalonia.Direct2D1.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Direct2D1.UnitTests", data.Parameters, false);

2
build/ReactiveUI.props

@ -1,5 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="reactiveui" Version="8.7.1" /> <PackageReference Include="reactiveui" Version="9.0.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

1
packages.cake

@ -167,6 +167,7 @@ public class Packages
new [] { "./src/", "Avalonia.Logging.Serilog"}, new [] { "./src/", "Avalonia.Logging.Serilog"},
new [] { "./src/", "Avalonia.Visuals"}, new [] { "./src/", "Avalonia.Visuals"},
new [] { "./src/", "Avalonia.Styling"}, new [] { "./src/", "Avalonia.Styling"},
new [] { "./src/", "Avalonia.OpenGL"},
new [] { "./src/", "Avalonia.Themes.Default"}, new [] { "./src/", "Avalonia.Themes.Default"},
new [] { "./src/Markup/", "Avalonia.Markup"}, new [] { "./src/Markup/", "Avalonia.Markup"},
new [] { "./src/Markup/", "Avalonia.Markup.Xaml"}, new [] { "./src/Markup/", "Avalonia.Markup.Xaml"},

9
samples/ControlCatalog/App.xaml

@ -14,6 +14,11 @@
<Setter Property="FontSize" Value="13"/> <Setter Property="FontSize" Value="13"/>
</Style> </Style>
<StyleInclude Source="resm:ControlCatalog.SideBar.xaml"/> <Style Selector="TextBlock.h3">
<Setter Property="Foreground" Value="#a2a2a2"/>
<Setter Property="FontSize" Value="13"/>
</Style>
<StyleInclude Source="resm:ControlCatalog.SideBar.xaml"/>
</Application.Styles> </Application.Styles>
</Application> </Application>

61
samples/ControlCatalog/Pages/MenuPage.xaml

@ -7,29 +7,46 @@
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Spacing="16"> Spacing="16">
<Menu> <StackPanel>
<MenuItem Header="_First"> <TextBlock Classes="h3" Margin="4 8">Defined in XAML</TextBlock>
<MenuItem Header="Standard _Menu Item"/> <Menu>
<Separator/> <MenuItem Header="_First">
<MenuItem Header="Menu with _Submenu"> <MenuItem Header="Standard _Menu Item"/>
<MenuItem Header="Submenu _1"/> <Separator/>
<MenuItem Header="Submenu _2"/> <MenuItem Header="Menu with _Submenu">
<MenuItem Header="Submenu _1"/>
<MenuItem Header="Submenu _2"/>
</MenuItem>
<MenuItem Header="Menu Item with _Icon">
<MenuItem.Icon>
<Image Source="resm:ControlCatalog.Assets.github_icon.png"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Menu Item with _Checkbox">
<MenuItem.Icon>
<CheckBox BorderThickness="0" IsHitTestVisible="False" IsChecked="True"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Menu Item with _Icon"> <MenuItem Header="_Second">
<MenuItem.Icon> <MenuItem Header="Second _Menu Item"/>
<Image Source="resm:ControlCatalog.Assets.github_icon.png"/>
</MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Menu Item with _Checkbox"> </Menu>
<MenuItem.Icon> </StackPanel>
<CheckBox BorderThickness="0" IsHitTestVisible="False" IsChecked="True"/>
</MenuItem.Icon> <StackPanel>
</MenuItem> <TextBlock Classes="h3" Margin="4 8">Dyanamically generated</TextBlock>
</MenuItem> <Menu Items="{Binding MenuItems}">
<MenuItem Header="_Second"> <Menu.Styles>
<MenuItem Header="Second _Menu Item"/> <Style Selector="MenuItem">
</MenuItem> <Setter Property="Header" Value="{Binding Header}"/>
</Menu> <Setter Property="Items" Value="{Binding Items}"/>
<Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
</Style>
</Menu.Styles>
</Menu>
</StackPanel>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

97
samples/ControlCatalog/Pages/MenuPage.xaml.cs

@ -1,5 +1,10 @@
using System.Collections.Generic;
using System.Reactive;
using System.Threading.Tasks;
using System.Windows.Input;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using ReactiveUI;
namespace ControlCatalog.Pages namespace ControlCatalog.Pages
{ {
@ -8,6 +13,51 @@ namespace ControlCatalog.Pages
public MenuPage() public MenuPage()
{ {
this.InitializeComponent(); this.InitializeComponent();
var vm = new MenuPageViewModel();
vm.MenuItems = new[]
{
new MenuItemViewModel
{
Header = "_File",
Items = new[]
{
new MenuItemViewModel { Header = "_Open...", Command = vm.OpenCommand },
new MenuItemViewModel { Header = "Save", Command = vm.SaveCommand },
new MenuItemViewModel { Header = "-" },
new MenuItemViewModel
{
Header = "Recent",
Items = new[]
{
new MenuItemViewModel
{
Header = "File1.txt",
Command = vm.OpenRecentCommand,
CommandParameter = @"c:\foo\File1.txt"
},
new MenuItemViewModel
{
Header = "File2.txt",
Command = vm.OpenRecentCommand,
CommandParameter = @"c:\foo\File2.txt"
},
}
},
}
},
new MenuItemViewModel
{
Header = "_Edit",
Items = new[]
{
new MenuItemViewModel { Header = "_Copy" },
new MenuItemViewModel { Header = "_Paste" },
}
}
};
DataContext = vm;
} }
private void InitializeComponent() private void InitializeComponent()
@ -15,4 +65,51 @@ namespace ControlCatalog.Pages
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
} }
public class MenuPageViewModel
{
public MenuPageViewModel()
{
OpenCommand = ReactiveCommand.CreateFromTask(Open);
SaveCommand = ReactiveCommand.Create(Save);
OpenRecentCommand = ReactiveCommand.Create<string>(OpenRecent);
}
public IReadOnlyList<MenuItemViewModel> MenuItems { get; set; }
public ReactiveCommand<Unit, Unit> OpenCommand { get; }
public ReactiveCommand<Unit, Unit> SaveCommand { get; }
public ReactiveCommand<string, Unit> OpenRecentCommand { get; }
public async Task Open()
{
var dialog = new OpenFileDialog();
var result = await dialog.ShowAsync();
if (result != null)
{
foreach (var path in result)
{
System.Diagnostics.Debug.WriteLine($"Opened: {path}");
}
}
}
public void Save()
{
System.Diagnostics.Debug.WriteLine("Save");
}
public void OpenRecent(string path)
{
System.Diagnostics.Debug.WriteLine($"Open recent: {path}");
}
}
public class MenuItemViewModel
{
public string Header { get; set; }
public ICommand Command { get; set; }
public object CommandParameter { get; set; }
public IList<MenuItemViewModel> Items { get; set; }
}
} }

7
samples/RenderDemo/Pages/AnimationsPage.xaml

@ -107,9 +107,12 @@
</UserControl.Styles> </UserControl.Styles>
<Grid> <Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" ClipToBounds="False"> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" ClipToBounds="False">
<StackPanel.Clock>
<Clock />
</StackPanel.Clock>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center"> <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock VerticalAlignment="Center">Hover to activate Transform Keyframe Animations.</TextBlock> <TextBlock VerticalAlignment="Center">Hover to activate Transform Keyframe Animations.</TextBlock>
<Button Content="{Binding PlayStateText}" Command="{Binding ToggleGlobalPlayState}"/> <Button Content="{Binding PlayStateText}" Command="{Binding TogglePlayState}" Click="ToggleClock" />
</StackPanel> </StackPanel>
<WrapPanel ClipToBounds="False"> <WrapPanel ClipToBounds="False">
<Border Classes="Test Rect1" Background="DarkRed"/> <Border Classes="Test Rect1" Background="DarkRed"/>
@ -120,4 +123,4 @@
</WrapPanel> </WrapPanel>
</StackPanel> </StackPanel>
</Grid> </Grid>
</UserControl> </UserControl>

16
samples/RenderDemo/Pages/AnimationsPage.xaml.cs

@ -5,6 +5,7 @@ using Avalonia.Controls;
using Avalonia.Controls.Shapes; using Avalonia.Controls.Shapes;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Media; using Avalonia.Media;
using RenderDemo.ViewModels; using RenderDemo.ViewModels;
@ -23,5 +24,20 @@ namespace RenderDemo.Pages
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
private void ToggleClock(object sender, RoutedEventArgs args)
{
var button = sender as Button;
var clock = button.Clock;
if (clock.PlayState == PlayState.Run)
{
clock.PlayState = PlayState.Pause;
}
else if (clock.PlayState == PlayState.Pause)
{
clock.PlayState = PlayState.Run;
}
}
} }
} }

26
samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs

@ -6,27 +6,15 @@ namespace RenderDemo.ViewModels
{ {
public class AnimationsPageViewModel : ReactiveObject public class AnimationsPageViewModel : ReactiveObject
{ {
private string _playStateText = "Pause all animations"; private bool _isPlaying = true;
public AnimationsPageViewModel() private string _playStateText = "Pause animations on this page";
{
ToggleGlobalPlayState = ReactiveCommand.Create(() => TogglePlayState());
}
void TogglePlayState() public void TogglePlayState()
{ {
switch (Animation.GlobalPlayState) PlayStateText = _isPlaying
{ ? "Resume animations on this page" : "Pause animations on this page";
case PlayState.Run: _isPlaying = !_isPlaying;
PlayStateText = "Resume all animations";
Animation.GlobalPlayState = PlayState.Pause;
break;
case PlayState.Pause:
PlayStateText = "Pause all animations";
Animation.GlobalPlayState = PlayState.Run;
break;
}
} }
public string PlayStateText public string PlayStateText
@ -34,7 +22,5 @@ namespace RenderDemo.ViewModels
get { return _playStateText; } get { return _playStateText; }
set { this.RaiseAndSetIfChanged(ref _playStateText, value); } set { this.RaiseAndSetIfChanged(ref _playStateText, value); }
} }
public ReactiveCommand ToggleGlobalPlayState { get; }
} }
} }

1
samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs

@ -7,6 +7,7 @@ using System.Linq;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using ReactiveUI.Legacy;
using ReactiveUI; using ReactiveUI;
namespace VirtualizationDemo.ViewModels namespace VirtualizationDemo.ViewModels

278
samples/interop/Direct3DInteropSample/MainWindow.cs

@ -1,81 +1,83 @@
using System; // Copyright (c) The Avalonia Project. All rights reserved.
using System.Collections.Generic; // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Linq;
using System.Text; using System;
using System.Threading.Tasks;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Direct2D1;
using Avalonia.Direct2D1.Media; using Avalonia.Direct2D1.Media;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using SharpDX; using SharpDX;
using SharpDX.D3DCompiler; using SharpDX.D3DCompiler;
using SharpDX.Direct2D1; using SharpDX.Direct2D1;
using SharpDX.Direct3D; using SharpDX.Direct3D;
using SharpDX.Direct3D11; using SharpDX.Direct3D11;
using SharpDX.DXGI; using SharpDX.DXGI;
using SharpDX.WIC;
using SharpDX.Mathematics;
using AlphaMode = SharpDX.Direct2D1.AlphaMode; using AlphaMode = SharpDX.Direct2D1.AlphaMode;
using Buffer = SharpDX.Direct3D11.Buffer; using Buffer = SharpDX.Direct3D11.Buffer;
using DeviceContext = SharpDX.Direct3D11.DeviceContext; using DeviceContext = SharpDX.Direct2D1.DeviceContext;
using Factory1 = SharpDX.DXGI.Factory1; using Factory2 = SharpDX.DXGI.Factory2;
using InputElement = SharpDX.Direct3D11.InputElement; using InputElement = SharpDX.Direct3D11.InputElement;
using Matrix = SharpDX.Matrix; using Matrix = SharpDX.Matrix;
using PixelFormat = SharpDX.Direct2D1.PixelFormat; using PixelFormat = SharpDX.Direct2D1.PixelFormat;
using Resource = SharpDX.Direct3D11.Resource;
namespace Direct3DInteropSample namespace Direct3DInteropSample
{ {
class MainWindow : Window public class MainWindow : Window
{ {
private SharpDX.Direct3D11.Device _d3dDevice; Texture2D _backBuffer;
private SharpDX.DXGI.Device _dxgiDevice; RenderTargetView _renderView;
Texture2D backBuffer = null; Texture2D _depthBuffer;
RenderTargetView renderView = null; DepthStencilView _depthView;
Texture2D depthBuffer = null;
DepthStencilView depthView = null;
private readonly SwapChain _swapChain; private readonly SwapChain _swapChain;
private SwapChainDescription _desc; private SwapChainDescription1 _desc;
private Matrix _proj = Matrix.Identity; private Matrix _proj = Matrix.Identity;
private Matrix _view; private readonly Matrix _view;
private Buffer _contantBuffer; private Buffer _contantBuffer;
private SharpDX.Direct2D1.Device _d2dDevice; private DeviceContext _deviceContext;
private SharpDX.Direct2D1.DeviceContext _d2dContext; private readonly MainWindowViewModel _model;
private RenderTarget _d2dRenderTarget;
private MainWindowViewModel _model;
public MainWindow() public MainWindow()
{ {
_dxgiDevice = AvaloniaLocator.Current.GetService<SharpDX.DXGI.Device>();
_d3dDevice = _dxgiDevice.QueryInterface<SharpDX.Direct3D11.Device>();
_d2dDevice = AvaloniaLocator.Current.GetService<SharpDX.Direct2D1.Device>();
DataContext = _model = new MainWindowViewModel(); DataContext = _model = new MainWindowViewModel();
_desc = new SwapChainDescription()
_desc = new SwapChainDescription1()
{ {
BufferCount = 1, BufferCount = 1,
ModeDescription = Width = (int)ClientSize.Width,
new ModeDescription((int)ClientSize.Width, (int)ClientSize.Height, Height = (int)ClientSize.Height,
new Rational(60, 1), Format.R8G8B8A8_UNorm), Format = Format.R8G8B8A8_UNorm,
IsWindowed = true,
OutputHandle = PlatformImpl?.Handle.Handle ?? IntPtr.Zero,
SampleDescription = new SampleDescription(1, 0), SampleDescription = new SampleDescription(1, 0),
SwapEffect = SwapEffect.Discard, SwapEffect = SwapEffect.Discard,
Usage = Usage.RenderTargetOutput Usage = Usage.RenderTargetOutput
}; };
_swapChain = new SwapChain(new Factory1(), _d3dDevice, _desc); using (var factory = Direct2D1Platform.DxgiDevice.Adapter.GetParent<Factory2>())
{
_swapChain = new SwapChain1(factory, Direct2D1Platform.DxgiDevice, PlatformImpl?.Handle.Handle ?? IntPtr.Zero, ref _desc);
}
_d2dContext = new SharpDX.Direct2D1.DeviceContext(_d2dDevice, DeviceContextOptions.None) _deviceContext = new DeviceContext(Direct2D1Platform.Direct2D1Device, DeviceContextOptions.None)
{ {
DotsPerInch = new Size2F(96, 96) DotsPerInch = new Size2F(96, 96)
}; };
CreateMesh(); CreateMesh();
_view = Matrix.LookAtLH(new Vector3(0, 0, -5), new Vector3(0, 0, 0), Vector3.UnitY); _view = Matrix.LookAtLH(new Vector3(0, 0, -5), new Vector3(0, 0, 0), Vector3.UnitY);
this.GetObservable(ClientSizeProperty).Subscribe(Resize); this.GetObservable(ClientSizeProperty).Subscribe(Resize);
Resize(ClientSize); Resize(ClientSize);
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
Background = Avalonia.Media.Brushes.Transparent; Background = Avalonia.Media.Brushes.Transparent;
} }
@ -83,29 +85,32 @@ namespace Direct3DInteropSample
protected override void HandlePaint(Rect rect) protected override void HandlePaint(Rect rect)
{ {
var viewProj = Matrix.Multiply(_view, _proj); var viewProj = Matrix.Multiply(_view, _proj);
var context = _d3dDevice.ImmediateContext; var context = Direct2D1Platform.Direct3D11Device.ImmediateContext;
// Clear views // Clear views
context.ClearDepthStencilView(depthView, DepthStencilClearFlags.Depth, 1.0f, 0); context.ClearDepthStencilView(_depthView, DepthStencilClearFlags.Depth, 1.0f, 0);
context.ClearRenderTargetView(renderView, Color.White); context.ClearRenderTargetView(_renderView, Color.White);
// Update WorldViewProj Matrix // Update WorldViewProj Matrix
var worldViewProj = Matrix.RotationX((float) _model.RotationX) * Matrix.RotationY((float) _model.RotationY) * var worldViewProj = Matrix.RotationX((float)_model.RotationX) * Matrix.RotationY((float)_model.RotationY)
Matrix.RotationZ((float) _model.RotationZ) * Matrix.RotationZ((float)_model.RotationZ)
* Matrix.Scaling((float) _model.Zoom) * viewProj; * Matrix.Scaling((float)_model.Zoom)
* viewProj;
worldViewProj.Transpose(); worldViewProj.Transpose();
context.UpdateSubresource(ref worldViewProj, _contantBuffer); context.UpdateSubresource(ref worldViewProj, _contantBuffer);
// Draw the cube // Draw the cube
context.Draw(36, 0); context.Draw(36, 0);
base.HandlePaint(rect); base.HandlePaint(rect);
// Present! // Present!
_swapChain.Present(0, PresentFlags.None); _swapChain.Present(0, PresentFlags.None);
} }
private void CreateMesh()
void CreateMesh()
{ {
var device = _d3dDevice; var device = Direct2D1Platform.Direct3D11Device;
// Compile Vertex and Pixel shaders // Compile Vertex and Pixel shaders
var vertexShaderByteCode = ShaderBytecode.CompileFromFile("MiniCube.fx", "VS", "vs_4_0"); var vertexShaderByteCode = ShaderBytecode.CompileFromFile("MiniCube.fx", "VS", "vs_4_0");
var vertexShader = new VertexShader(device, vertexShaderByteCode); var vertexShader = new VertexShader(device, vertexShaderByteCode);
@ -114,63 +119,72 @@ namespace Direct3DInteropSample
var pixelShader = new PixelShader(device, pixelShaderByteCode); var pixelShader = new PixelShader(device, pixelShaderByteCode);
var signature = ShaderSignature.GetInputSignature(vertexShaderByteCode); var signature = ShaderSignature.GetInputSignature(vertexShaderByteCode);
var inputElements = new[]
{
new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0),
new InputElement("COLOR", 0, Format.R32G32B32A32_Float, 16, 0)
};
// Layout from VertexShader input signature // Layout from VertexShader input signature
var layout = new InputLayout(device, signature, new[] var layout = new InputLayout(
{ device,
new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0), signature,
new InputElement("COLOR", 0, Format.R32G32B32A32_Float, 16, 0) inputElements);
});
// Instantiate Vertex buffer from vertex data
// Instantiate Vertex buiffer from vertex data var vertices = Buffer.Create(
var vertices = Buffer.Create(device, BindFlags.VertexBuffer, new[] device,
{ BindFlags.VertexBuffer,
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), // Front new[]
new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), {
new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), // Front
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), // BACK new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), // BACK
new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), // Top new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), // Top
new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4(-1.0f,-1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), // Bottom new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f,-1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f,-1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), // Bottom
new Vector4(-1.0f,-1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4( 1.0f,-1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4( 1.0f,-1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), // Left new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), // Left
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), // Right new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), // Right
new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
}); new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
});
// Create Constant Buffer // Create Constant Buffer
_contantBuffer = new Buffer(device, Utilities.SizeOf<Matrix>(), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); _contantBuffer = new Buffer(device, Utilities.SizeOf<Matrix>(), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
var context = _d3dDevice.ImmediateContext; var context = Direct2D1Platform.Direct3D11Device.ImmediateContext;
// Prepare All the stages // Prepare All the stages
context.InputAssembler.InputLayout = layout; context.InputAssembler.InputLayout = layout;
@ -181,63 +195,73 @@ namespace Direct3DInteropSample
context.PixelShader.Set(pixelShader); context.PixelShader.Set(pixelShader);
} }
void Resize(Size size) private void Resize(Size size)
{ {
Utilities.Dispose(ref _d2dRenderTarget); Utilities.Dispose(ref _deviceContext);
Utilities.Dispose(ref backBuffer); Utilities.Dispose(ref _backBuffer);
Utilities.Dispose(ref renderView); Utilities.Dispose(ref _renderView);
Utilities.Dispose(ref depthBuffer); Utilities.Dispose(ref _depthBuffer);
Utilities.Dispose(ref depthView); Utilities.Dispose(ref _depthView);
var context = _d3dDevice.ImmediateContext; var context = Direct2D1Platform.Direct3D11Device.ImmediateContext;
// Resize the backbuffer // Resize the backbuffer
_swapChain.ResizeBuffers(_desc.BufferCount, (int)size.Width, (int)size.Height, Format.Unknown, SwapChainFlags.None); _swapChain.ResizeBuffers(0, 0, 0, Format.Unknown, SwapChainFlags.None);
// Get the backbuffer from the swapchain // Get the backbuffer from the swapchain
backBuffer = Texture2D.FromSwapChain<Texture2D>(_swapChain, 0); _backBuffer = Resource.FromSwapChain<Texture2D>(_swapChain, 0);
// Renderview on the backbuffer // Renderview on the backbuffer
renderView = new RenderTargetView(_d3dDevice, backBuffer); _renderView = new RenderTargetView(Direct2D1Platform.Direct3D11Device, _backBuffer);
// Create the depth buffer // Create the depth buffer
depthBuffer = new Texture2D(_d3dDevice, new Texture2DDescription() _depthBuffer = new Texture2D(
{ Direct2D1Platform.Direct3D11Device,
Format = Format.D32_Float_S8X24_UInt, new Texture2DDescription()
ArraySize = 1, {
MipLevels = 1, Format = Format.D32_Float_S8X24_UInt,
Width = (int)size.Width, ArraySize = 1,
Height = (int)size.Height, MipLevels = 1,
SampleDescription = new SampleDescription(1, 0), Width = (int)size.Width,
Usage = ResourceUsage.Default, Height = (int)size.Height,
BindFlags = BindFlags.DepthStencil, SampleDescription = new SampleDescription(1, 0),
CpuAccessFlags = CpuAccessFlags.None, Usage = ResourceUsage.Default,
OptionFlags = ResourceOptionFlags.None BindFlags = BindFlags.DepthStencil,
}); CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None
});
// Create the depth buffer view // Create the depth buffer view
depthView = new DepthStencilView(_d3dDevice, depthBuffer); _depthView = new DepthStencilView(Direct2D1Platform.Direct3D11Device, _depthBuffer);
// Setup targets and viewport for rendering // Setup targets and viewport for rendering
context.Rasterizer.SetViewport(new Viewport(0, 0, (int)size.Width, (int)size.Height, 0.0f, 1.0f)); context.Rasterizer.SetViewport(new Viewport(0, 0, (int)size.Width, (int)size.Height, 0.0f, 1.0f));
context.OutputMerger.SetTargets(depthView, renderView); context.OutputMerger.SetTargets(_depthView, _renderView);
// Setup new projection matrix with correct aspect ratio // Setup new projection matrix with correct aspect ratio
_proj = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, (float)(size.Width / size.Height), 0.1f, 100.0f); _proj = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, (float)(size.Width / size.Height), 0.1f, 100.0f);
using (var dxgiBackBuffer = _swapChain.GetBackBuffer<Surface>(0)) using (var dxgiBackBuffer = _swapChain.GetBackBuffer<Surface>(0))
{ {
_d2dRenderTarget = new RenderTarget(AvaloniaLocator.Current.GetService<SharpDX.Direct2D1.Factory>() var renderTarget = new SharpDX.Direct2D1.RenderTarget(
, dxgiBackBuffer, new RenderTargetProperties Direct2D1Platform.Direct2D1Factory,
dxgiBackBuffer,
new RenderTargetProperties
{ {
DpiX = 96, DpiX = 96,
DpiY = 96, DpiY = 96,
Type = RenderTargetType.Default, Type = RenderTargetType.Default,
PixelFormat = new PixelFormat(Format.Unknown, AlphaMode.Premultiplied) PixelFormat = new PixelFormat(
Format.Unknown,
AlphaMode.Premultiplied)
}); });
}
_deviceContext = renderTarget.QueryInterface<DeviceContext>();
renderTarget.Dispose();
}
} }
class D3DRenderTarget: IRenderTarget private class D3DRenderTarget : IRenderTarget
{ {
private readonly MainWindow _window; private readonly MainWindow _window;
@ -245,16 +269,14 @@ namespace Direct3DInteropSample
{ {
_window = window; _window = window;
} }
public void Dispose() public void Dispose()
{ {
} }
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
{ {
return new DrawingContextImpl(visualBrushRenderer, null, _window._d2dRenderTarget, return new DrawingContextImpl(visualBrushRenderer, null, _window._deviceContext);
AvaloniaLocator.Current.GetService<SharpDX.DirectWrite.Factory>(),
AvaloniaLocator.Current.GetService<ImagingFactory>());
} }
} }

3
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -52,7 +52,8 @@ namespace Avalonia.Android
.Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>() .Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
.Bind<IWindowingPlatform>().ToConstant(Instance) .Bind<IWindowingPlatform>().ToConstant(Instance)
.Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>() .Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>()
.Bind<IRenderLoop>().ToConstant(new DefaultRenderLoop(60)) .Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<IAssetLoader>().ToConstant(new AssetLoader(app.GetType().Assembly)); .Bind<IAssetLoader>().ToConstant(new AssetLoader(app.GetType().Assembly));
SkiaPlatform.Initialize(); SkiaPlatform.Initialize();

28
src/Avalonia.Animation/Animatable.cs

@ -14,26 +14,14 @@ namespace Avalonia.Animation
/// Base class for all animatable objects. /// Base class for all animatable objects.
/// </summary> /// </summary>
public class Animatable : AvaloniaObject public class Animatable : AvaloniaObject
{ {
/// <summary> public static readonly StyledProperty<IClock> ClockProperty =
/// Defines the <see cref="PlayState"/> property. AvaloniaProperty.Register<Animatable, IClock>(nameof(Clock), inherits: true);
/// </summary>
public static readonly DirectProperty<Animatable, PlayState> PlayStateProperty =
AvaloniaProperty.RegisterDirect<Animatable, PlayState>(
nameof(PlayState),
o => o.PlayState,
(o, v) => o.PlayState = v);
private PlayState _playState = PlayState.Run;
/// <summary> public IClock Clock
/// Gets or sets the state of the animation for this
/// control.
/// </summary>
public PlayState PlayState
{ {
get { return _playState; } get => GetValue(ClockProperty);
set { SetAndRaise(PlayStateProperty, ref _playState, value); } set => SetValue(ClockProperty, value);
} }
/// <summary> /// <summary>
@ -69,9 +57,9 @@ namespace Avalonia.Animation
if (match != null) if (match != null)
{ {
match.Apply(this, e.OldValue, e.NewValue); match.Apply(this, Clock ?? Avalonia.Animation.Clock.GlobalClock, e.OldValue, e.NewValue);
} }
} }
} }
} }
} }

15
src/Avalonia.Animation/Animation.cs

@ -17,11 +17,6 @@ namespace Avalonia.Animation
/// </summary> /// </summary>
public class Animation : AvaloniaList<KeyFrame>, IAnimation public class Animation : AvaloniaList<KeyFrame>, IAnimation
{ {
/// <summary>
/// Gets or sets the animation play state for all animations
/// </summary>
public static PlayState GlobalPlayState { get; set; } = PlayState.Run;
/// <summary> /// <summary>
/// Gets or sets the active time of this animation. /// Gets or sets the active time of this animation.
/// </summary> /// </summary>
@ -149,12 +144,12 @@ namespace Avalonia.Animation
} }
/// <inheritdocs/> /// <inheritdocs/>
public IDisposable Apply(Animatable control, IObservable<bool> match, Action onComplete) public IDisposable Apply(Animatable control, IClock clock, IObservable<bool> match, Action onComplete)
{ {
var (animators, subscriptions) = InterpretKeyframes(control); var (animators, subscriptions) = InterpretKeyframes(control);
if (animators.Count == 1) if (animators.Count == 1)
{ {
subscriptions.Add(animators[0].Apply(this, control, match, onComplete)); subscriptions.Add(animators[0].Apply(this, control, clock, match, onComplete));
} }
else else
{ {
@ -168,7 +163,7 @@ namespace Avalonia.Animation
animatorOnComplete = () => tcs.SetResult(null); animatorOnComplete = () => tcs.SetResult(null);
completionTasks.Add(tcs.Task); completionTasks.Add(tcs.Task);
} }
subscriptions.Add(animator.Apply(this, control, match, animatorOnComplete)); subscriptions.Add(animator.Apply(this, control, clock, match, animatorOnComplete));
} }
if (onComplete != null) if (onComplete != null)
@ -180,7 +175,7 @@ namespace Avalonia.Animation
} }
/// <inheritdocs/> /// <inheritdocs/>
public Task RunAsync(Animatable control) public Task RunAsync(Animatable control, IClock clock = null)
{ {
var run = new TaskCompletionSource<object>(); var run = new TaskCompletionSource<object>();
@ -188,7 +183,7 @@ namespace Avalonia.Animation
run.SetException(new InvalidOperationException("Looping animations must not use the Run method.")); run.SetException(new InvalidOperationException("Looping animations must not use the Run method."));
IDisposable subscriptions = null; IDisposable subscriptions = null;
subscriptions = this.Apply(control, Observable.Return(true), () => subscriptions = this.Apply(control, clock, Observable.Return(true), () =>
{ {
run.SetResult(null); run.SetResult(null);
subscriptions?.Dispose(); subscriptions?.Dispose();

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

@ -8,7 +8,7 @@ using Avalonia.Reactive;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
/// <summary> /// <summary>
/// Handles interpolatoin and time-related functions /// Handles interpolation and time-related functions
/// for keyframe animations. /// for keyframe animations.
/// </summary> /// </summary>
internal class AnimationInstance<T> : SingleSubscriberObservableBase<T> internal class AnimationInstance<T> : SingleSubscriberObservableBase<T>
@ -19,7 +19,6 @@ namespace Avalonia.Animation
private double _currentIteration; private double _currentIteration;
private bool _isLooping; private bool _isLooping;
private bool _gotFirstKFValue; private bool _gotFirstKFValue;
private bool _gotFirstFrameCount;
private bool _iterationDelay; private bool _iterationDelay;
private FillMode _fillMode; private FillMode _fillMode;
private PlaybackDirection _animationDirection; private PlaybackDirection _animationDirection;
@ -29,15 +28,14 @@ namespace Avalonia.Animation
private double _speedRatio; private double _speedRatio;
private TimeSpan _delay; private TimeSpan _delay;
private TimeSpan _duration; private TimeSpan _duration;
private TimeSpan _firstFrameCount;
private TimeSpan _internalClock;
private TimeSpan? _previousClock;
private Easings.Easing _easeFunc; private Easings.Easing _easeFunc;
private Action _onCompleteAction; private Action _onCompleteAction;
private Func<double, T, T> _interpolator; private Func<double, T, T> _interpolator;
private IDisposable _timerSubscription; private IDisposable _timerSubscription;
private readonly IClock _baseClock;
private IClock _clock;
public AnimationInstance(Animation animation, Animatable control, Animator<T> animator, Action OnComplete, Func<double, T, T> Interpolator) public AnimationInstance(Animation animation, Animatable control, Animator<T> animator, IClock baseClock, Action OnComplete, Func<double, T, T> Interpolator)
{ {
if (animation.SpeedRatio <= 0) if (animation.SpeedRatio <= 0)
throw new InvalidOperationException("Speed ratio cannot be negative or zero."); throw new InvalidOperationException("Speed ratio cannot be negative or zero.");
@ -73,17 +71,19 @@ namespace Avalonia.Animation
_fillMode = animation.FillMode; _fillMode = animation.FillMode;
_onCompleteAction = OnComplete; _onCompleteAction = OnComplete;
_interpolator = Interpolator; _interpolator = Interpolator;
_baseClock = baseClock;
} }
protected override void Unsubscribed() protected override void Unsubscribed()
{ {
_timerSubscription?.Dispose(); _timerSubscription?.Dispose();
_clock.PlayState = PlayState.Stop;
} }
protected override void Subscribed() protected override void Subscribed()
{ {
_timerSubscription = Timing.AnimationsTimer _clock = new Clock(_baseClock);
.Subscribe(p => this.Step(p)); _timerSubscription = _clock.Subscribe(Step);
} }
public void Step(TimeSpan frameTick) public void Step(TimeSpan frameTick)
@ -116,46 +116,21 @@ namespace Avalonia.Animation
PublishNext(_lastInterpValue); PublishNext(_lastInterpValue);
} }
private void DoPlayStatesAndTime(TimeSpan systemTime) private void DoPlayStates()
{ {
if (Animation.GlobalPlayState == PlayState.Stop || _targetControl.PlayState == PlayState.Stop) if (_clock.PlayState == PlayState.Stop || _baseClock.PlayState == PlayState.Stop)
DoComplete(); DoComplete();
if (!_previousClock.HasValue)
{
_previousClock = systemTime;
_internalClock = TimeSpan.Zero;
}
else
{
if (Animation.GlobalPlayState == PlayState.Pause || _targetControl.PlayState == PlayState.Pause)
{
_previousClock = systemTime;
return;
}
var delta = systemTime - _previousClock;
_internalClock += delta.Value;
_previousClock = systemTime;
}
if (!_gotFirstKFValue) if (!_gotFirstKFValue)
{ {
_firstKFValue = (T)_parent.First().Value; _firstKFValue = (T)_parent.First().Value;
_gotFirstKFValue = true; _gotFirstKFValue = true;
} }
if (!_gotFirstFrameCount)
{
_firstFrameCount = _internalClock;
_gotFirstFrameCount = true;
}
} }
private void InternalStep(TimeSpan systemTime) private void InternalStep(TimeSpan time)
{ {
DoPlayStatesAndTime(systemTime); DoPlayStates();
var time = _internalClock - _firstFrameCount;
var delayEndpoint = _delay; var delayEndpoint = _delay;
var iterationEndpoint = delayEndpoint + _duration; var iterationEndpoint = delayEndpoint + _duration;
@ -176,22 +151,18 @@ namespace Avalonia.Animation
} }
//Calculate the current iteration number //Calculate the current iteration number
_currentIteration = (int)Math.Floor((double)time.Ticks / iterationEndpoint.Ticks) + 2; _currentIteration = (int)Math.Floor((double)((double)time.Ticks / iterationEndpoint.Ticks)) + 2;
} }
else else
{ {
_previousClock = systemTime;
return; return;
} }
time = TimeSpan.FromTicks(time.Ticks % iterationEndpoint.Ticks); time = TimeSpan.FromTicks((long)(time.Ticks % iterationEndpoint.Ticks));
if (!_isLooping) if (!_isLooping)
{ {
if (_currentIteration > _repeatCount) if ((_currentIteration > _repeatCount) || (time > iterationEndpoint))
DoComplete();
if (time > iterationEndpoint)
DoComplete(); DoComplete();
} }
@ -225,4 +196,4 @@ namespace Avalonia.Animation
} }
} }
} }
} }

118
src/Avalonia.Animation/Animator`1.cs

@ -1,10 +1,14 @@
using System; // 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.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Animation.Utils; using Avalonia.Animation.Utils;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Reactive;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
@ -17,7 +21,7 @@ namespace Avalonia.Animation
/// List of type-converted keyframes. /// List of type-converted keyframes.
/// </summary> /// </summary>
private readonly List<AnimatorKeyFrame> _convertedKeyframes = new List<AnimatorKeyFrame>(); private readonly List<AnimatorKeyFrame> _convertedKeyframes = new List<AnimatorKeyFrame>();
private bool _isVerifiedAndConverted; private bool _isVerifiedAndConverted;
/// <summary> /// <summary>
@ -28,21 +32,17 @@ namespace Avalonia.Animation
public Animator() public Animator()
{ {
// Invalidate keyframes when changed. // Invalidate keyframes when changed.
this.CollectionChanged += delegate { _isVerifiedAndConverted = false; }; this.CollectionChanged += delegate { _isVerifiedAndConverted = false; };
} }
/// <inheritdoc/> /// <inheritdoc/>
public virtual IDisposable Apply(Animation animation, Animatable control, IObservable<bool> match, Action onComplete) public virtual IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> match, Action onComplete)
{ {
if (!_isVerifiedAndConverted) if (!_isVerifiedAndConverted)
VerifyConvertKeyFrames(); VerifyConvertKeyFrames();
return match var subject = new DisposeAnimationInstanceSubject<T>(this, animation, control, clock, onComplete);
.Where(p => p) return match.Subscribe(subject);
.Subscribe(_ =>
{
var timerObs = RunKeyFrames(animation, control, onComplete);
});
} }
/// <summary> /// <summary>
@ -52,58 +52,84 @@ namespace Avalonia.Animation
/// (i.e., the normalized time between the selected keyframes, relative to the /// (i.e., the normalized time between the selected keyframes, relative to the
/// time parameter). /// time parameter).
/// </summary> /// </summary>
/// <param name="t">The time parameter, relative to the total animation time</param> /// <param name="animationTime">The time parameter, relative to the total animation time</param>
protected (double IntraKFTime, KeyFramePair<T> KFPair) GetKFPairAndIntraKFTime(double t) protected (double IntraKFTime, KeyFramePair<T> KFPair) GetKFPairAndIntraKFTime(double animationTime)
{ {
AnimatorKeyFrame firstCue, lastCue ; AnimatorKeyFrame firstKeyframe, lastKeyframe;
int kvCount = _convertedKeyframes.Count; int kvCount = _convertedKeyframes.Count;
if (kvCount > 2) if (kvCount > 2)
{ {
if (t <= 0.0) if (animationTime <= 0.0)
{ {
firstCue = _convertedKeyframes[0]; firstKeyframe = _convertedKeyframes[0];
lastCue = _convertedKeyframes[1]; lastKeyframe = _convertedKeyframes[1];
} }
else if (t >= 1.0) else if (animationTime >= 1.0)
{ {
firstCue = _convertedKeyframes[_convertedKeyframes.Count - 2]; firstKeyframe = _convertedKeyframes[_convertedKeyframes.Count - 2];
lastCue = _convertedKeyframes[_convertedKeyframes.Count - 1]; lastKeyframe = _convertedKeyframes[_convertedKeyframes.Count - 1];
} }
else else
{ {
(double time, int index) maxval = (0.0d, 0); int index = FindClosestBeforeKeyFrame(animationTime);
for (int i = 0; i < _convertedKeyframes.Count; i++) firstKeyframe = _convertedKeyframes[index];
{ lastKeyframe = _convertedKeyframes[index + 1];
var comp = _convertedKeyframes[i].Cue.CueValue;
if (t >= comp)
{
maxval = (comp, i);
}
}
firstCue = _convertedKeyframes[maxval.index];
lastCue = _convertedKeyframes[maxval.index + 1];
} }
} }
else else
{ {
firstCue = _convertedKeyframes[0]; firstKeyframe = _convertedKeyframes[0];
lastCue = _convertedKeyframes[1]; lastKeyframe = _convertedKeyframes[1];
} }
double t0 = firstCue.Cue.CueValue; double t0 = firstKeyframe.Cue.CueValue;
double t1 = lastCue.Cue.CueValue; double t1 = lastKeyframe.Cue.CueValue;
var intraframeTime = (t - t0) / (t1 - t0); var intraframeTime = (animationTime - t0) / (t1 - t0);
var firstFrameData = (firstCue.GetTypedValue<T>(), firstCue.isNeutral); var firstFrameData = (firstKeyframe.GetTypedValue<T>(), firstKeyframe.isNeutral);
var lastFrameData = (lastCue.GetTypedValue<T>(), lastCue.isNeutral); var lastFrameData = (lastKeyframe.GetTypedValue<T>(), lastKeyframe.isNeutral);
return (intraframeTime, new KeyFramePair<T>(firstFrameData, lastFrameData)); return (intraframeTime, new KeyFramePair<T>(firstFrameData, lastFrameData));
} }
private int FindClosestBeforeKeyFrame(double time)
{
int FindClosestBeforeKeyFrame(int startIndex, int length)
{
if (length == 0 || length == 1)
{
return startIndex;
}
int middle = startIndex + (length / 2);
if (_convertedKeyframes[middle].Cue.CueValue < time)
{
return FindClosestBeforeKeyFrame(middle, length - middle);
}
else if (_convertedKeyframes[middle].Cue.CueValue > time)
{
return FindClosestBeforeKeyFrame(startIndex, middle - startIndex);
}
else
{
return middle;
}
}
return FindClosestBeforeKeyFrame(0, _convertedKeyframes.Count);
}
/// <summary> /// <summary>
/// Runs the KeyFrames Animation. /// Runs the KeyFrames Animation.
/// </summary> /// </summary>
private IDisposable RunKeyFrames(Animation animation, Animatable control, Action onComplete) internal IDisposable Run(Animation animation, Animatable control, IClock clock, Action onComplete)
{ {
var instance = new AnimationInstance<T>(animation, control, this, onComplete, DoInterpolation); var instance = new AnimationInstance<T>(
animation,
control,
this,
clock ?? control.Clock ?? Clock.GlobalClock,
onComplete,
DoInterpolation);
return control.Bind<T>((AvaloniaProperty<T>)Property, instance, BindingPriority.Animation); return control.Bind<T>((AvaloniaProperty<T>)Property, instance, BindingPriority.Animation);
} }
@ -124,14 +150,6 @@ namespace Avalonia.Animation
AddNeutralKeyFramesIfNeeded(); AddNeutralKeyFramesIfNeeded();
var copy = _convertedKeyframes.ToList().OrderBy(p => p.Cue.CueValue);
_convertedKeyframes.Clear();
foreach (AnimatorKeyFrame keyframe in copy)
{
_convertedKeyframes.Add(keyframe);
}
_isVerifiedAndConverted = true; _isVerifiedAndConverted = true;
} }
@ -161,7 +179,7 @@ namespace Avalonia.Animation
{ {
if (!hasStartKey) if (!hasStartKey)
{ {
_convertedKeyframes.Add(new AnimatorKeyFrame(null, new Cue(0.0d)) { Value = default(T), isNeutral = true }); _convertedKeyframes.Insert(0, new AnimatorKeyFrame(null, new Cue(0.0d)) { Value = default(T), isNeutral = true });
} }
if (!hasEndKey) if (!hasEndKey)
@ -170,4 +188,4 @@ namespace Avalonia.Animation
} }
} }
} }
} }

30
src/Avalonia.Animation/Clock.cs

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Text;
using Avalonia.Reactive;
namespace Avalonia.Animation
{
public class Clock : ClockBase
{
public static IClock GlobalClock => AvaloniaLocator.Current.GetService<IGlobalClock>();
private IDisposable _parentSubscription;
public Clock()
:this(GlobalClock)
{
}
public Clock(IClock parent)
{
_parentSubscription = parent.Subscribe(Pulse);
}
protected override void Stop()
{
_parentSubscription?.Dispose();
}
}
}

72
src/Avalonia.Animation/ClockBase.cs

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Text;
using Avalonia.Reactive;
namespace Avalonia.Animation
{
public class ClockBase : IClock
{
private ClockObservable _observable;
private IObservable<TimeSpan> _connectedObservable;
private TimeSpan? _previousTime;
private TimeSpan _internalTime;
protected ClockBase()
{
_observable = new ClockObservable();
_connectedObservable = _observable.Publish().RefCount();
}
protected bool HasSubscriptions => _observable.HasSubscriptions;
public PlayState PlayState { get; set; }
protected void Pulse(TimeSpan systemTime)
{
if (!_previousTime.HasValue)
{
_previousTime = systemTime;
_internalTime = TimeSpan.Zero;
}
else
{
if (PlayState == PlayState.Pause)
{
_previousTime = systemTime;
return;
}
var delta = systemTime - _previousTime;
_internalTime += delta.Value;
_previousTime = systemTime;
}
_observable.Pulse(_internalTime);
if (PlayState == PlayState.Stop)
{
Stop();
}
}
protected virtual void Stop()
{
}
public IDisposable Subscribe(IObserver<TimeSpan> observer)
{
return _connectedObservable.Subscribe(observer);
}
private class ClockObservable : LightweightObservableBase<TimeSpan>
{
public bool HasSubscriptions { get; private set; }
public void Pulse(TimeSpan time) => PublishNext(time);
protected override void Initialize() => HasSubscriptions = true;
protected override void Deinitialize() => HasSubscriptions = false;
}
}
}

65
src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs

@ -0,0 +1,65 @@
// 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.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using Avalonia.Animation.Utils;
using Avalonia.Collections;
using Avalonia.Data;
using Avalonia.Reactive;
namespace Avalonia.Animation
{
/// <summary>
/// Manages the lifetime of animation instances as determined by its selector state.
/// </summary>
internal class DisposeAnimationInstanceSubject<T> : IObserver<bool>, IDisposable
{
private IDisposable _lastInstance;
private bool _lastMatch;
private Animator<T> _animator;
private Animation _animation;
private Animatable _control;
private Action _onComplete;
private IClock _clock;
public DisposeAnimationInstanceSubject(Animator<T> animator, Animation animation, Animatable control, IClock clock, Action onComplete)
{
this._animator = animator;
this._animation = animation;
this._control = control;
this._onComplete = onComplete;
this._clock = clock;
}
public void Dispose()
{
_lastInstance?.Dispose();
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
_lastInstance?.Dispose();
}
void IObserver<bool>.OnNext(bool matchVal)
{
if (matchVal != _lastMatch)
{
_lastInstance?.Dispose();
if (matchVal)
{
_lastInstance = _animator.Run(_animation, _control, _clock, _onComplete);
}
_lastMatch = matchVal;
}
}
}
}

5
src/Avalonia.Animation/DoubleAnimator.cs

@ -1,4 +1,7 @@
namespace Avalonia.Animation // 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.
namespace Avalonia.Animation
{ {
/// <summary> /// <summary>
/// Animator that handles <see cref="double"/> properties. /// Animator that handles <see cref="double"/> properties.

5
src/Avalonia.Animation/FillMode.cs

@ -1,4 +1,7 @@
namespace Avalonia.Animation // 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.
namespace Avalonia.Animation
{ {
public enum FillMode public enum FillMode
{ {

11
src/Avalonia.Animation/IAnimation.cs

@ -1,3 +1,6 @@
// 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;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -9,13 +12,13 @@ namespace Avalonia.Animation
public interface IAnimation public interface IAnimation
{ {
/// <summary> /// <summary>
/// Apply the animation to the specified control /// Apply the animation to the specified control and run it when <paramref name="match" /> produces <c>true</c>.
/// </summary> /// </summary>
IDisposable Apply(Animatable control, IObservable<bool> match, Action onComplete = null); IDisposable Apply(Animatable control, IClock clock, IObservable<bool> match, Action onComplete = null);
/// <summary> /// <summary>
/// Run the animation to the specified control /// Run the animation on the specified control.
/// </summary> /// </summary>
Task RunAsync(Animatable control); Task RunAsync(Animatable control, IClock clock);
} }
} }

3
src/Avalonia.Animation/IAnimationSetter.cs

@ -1,3 +1,6 @@
// 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.
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
public interface IAnimationSetter public interface IAnimationSetter

7
src/Avalonia.Animation/IAnimator.cs

@ -1,4 +1,7 @@
using System; // 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.Collections.Generic; using System.Collections.Generic;
namespace Avalonia.Animation namespace Avalonia.Animation
@ -16,6 +19,6 @@ namespace Avalonia.Animation
/// <summary> /// <summary>
/// Applies the current KeyFrame group to the specified control. /// Applies the current KeyFrame group to the specified control.
/// </summary> /// </summary>
IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch, Action onComplete); IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> match, Action onComplete);
} }
} }

11
src/Avalonia.Animation/IClock.cs

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Animation
{
public interface IClock : IObservable<TimeSpan>
{
PlayState PlayState { get; set; }
}
}

10
src/Avalonia.Animation/IGlobalClock.cs

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Animation
{
public interface IGlobalClock : IClock
{
}
}

2
src/Avalonia.Animation/ITransition.cs

@ -13,7 +13,7 @@ namespace Avalonia.Animation
/// <summary> /// <summary>
/// Applies the transition to the specified <see cref="Animatable"/>. /// Applies the transition to the specified <see cref="Animatable"/>.
/// </summary> /// </summary>
IDisposable Apply(Animatable control, object oldValue, object newValue); IDisposable Apply(Animatable control, IClock clock, object oldValue, object newValue);
/// <summary> /// <summary>
/// Gets the property to be animated. /// Gets the property to be animated.

5
src/Avalonia.Animation/KeyFrame.cs

@ -1,4 +1,7 @@
using System; // 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.Collections.Generic; using System.Collections.Generic;
using Avalonia.Collections; using Avalonia.Collections;

3
src/Avalonia.Animation/KeyFramePair`1.cs

@ -1,3 +1,6 @@
// 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.
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
/// <summary> /// <summary>

5
src/Avalonia.Animation/PlayState.cs

@ -1,4 +1,7 @@
namespace Avalonia.Animation // 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.
namespace Avalonia.Animation
{ {
/// <summary> /// <summary>
/// Determines the playback state of an animation. /// Determines the playback state of an animation.

5
src/Avalonia.Animation/PlaybackDirection.cs

@ -1,4 +1,7 @@
namespace Avalonia.Animation // 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.
namespace Avalonia.Animation
{ {
/// <summary> /// <summary>
/// Determines the playback direction of an animation. /// Determines the playback direction of an animation.

54
src/Avalonia.Animation/Timing.cs

@ -1,54 +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;
using System.Linq;
using System.Reactive.Linq;
using Avalonia.Threading;
namespace Avalonia.Animation
{
/// <summary>
/// Provides global timing functions for animations.
/// </summary>
public static class Timing
{
/// <summary>
/// The number of frames per second.
/// </summary>
public const int FramesPerSecond = 60;
/// <summary>
/// The time span of each frame.
/// </summary>
internal static readonly TimeSpan FrameTick = TimeSpan.FromSeconds(1.0 / FramesPerSecond);
/// <summary>
/// Initializes static members of the <see cref="Timing"/> class.
/// </summary>
static Timing()
{
var globalTimer = Observable.Interval(FrameTick, AvaloniaScheduler.Instance);
AnimationsTimer = globalTimer
.Select(_ => GetTickCount())
.Publish()
.RefCount();
}
internal static TimeSpan GetTickCount() => TimeSpan.FromMilliseconds(Environment.TickCount);
/// <summary>
/// Gets the animation timer.
/// </summary>
/// <remarks>
/// The animation timer triggers usually at 60 times per second or as
/// defined in <see cref="FramesPerSecond"/>.
/// The parameter passed to a subsciber is the current playstate of the animation.
/// </remarks>
internal static IObservable<TimeSpan> AnimationsTimer
{
get;
}
}
}

27
src/Avalonia.Animation/TransitionInstance.cs

@ -15,21 +15,22 @@ namespace Avalonia.Animation
/// </summary> /// </summary>
internal class TransitionInstance : SingleSubscriberObservableBase<double> internal class TransitionInstance : SingleSubscriberObservableBase<double>
{ {
private IDisposable timerSubscription; private IDisposable _timerSubscription;
private TimeSpan startTime; private TimeSpan _duration;
private TimeSpan duration; private readonly IClock _baseClock;
private IClock _clock;
public TransitionInstance(TimeSpan Duration) public TransitionInstance(IClock clock, TimeSpan Duration)
{ {
duration = Duration; _duration = Duration;
_baseClock = clock;
} }
private void TimerTick(TimeSpan t) private void TimerTick(TimeSpan t)
{ {
var interpVal = (double)(t.Ticks - startTime.Ticks) / duration.Ticks; var interpVal = (double)t.Ticks / _duration.Ticks;
if (interpVal > 1d if (interpVal > 1d || interpVal < 0d)
|| interpVal < 0d)
{ {
PublishCompleted(); PublishCompleted();
return; return;
@ -40,15 +41,15 @@ namespace Avalonia.Animation
protected override void Unsubscribed() protected override void Unsubscribed()
{ {
timerSubscription?.Dispose(); _timerSubscription?.Dispose();
_clock.PlayState = PlayState.Stop;
} }
protected override void Subscribed() protected override void Subscribed()
{ {
startTime = Timing.GetTickCount(); _clock = new Clock(_baseClock);
timerSubscription = Timing.AnimationsTimer _timerSubscription = _clock.Subscribe(TimerTick);
.Subscribe(t => TimerTick(t));
PublishNext(0.0d); PublishNext(0.0d);
} }
} }
} }

7
src/Avalonia.Animation/Transition`1.cs

@ -14,7 +14,6 @@ namespace Avalonia.Animation
public abstract class Transition<T> : AvaloniaObject, ITransition public abstract class Transition<T> : AvaloniaObject, ITransition
{ {
private AvaloniaProperty _prop; private AvaloniaProperty _prop;
private Easing _easing;
/// <summary> /// <summary>
/// Gets the duration of the animation. /// Gets the duration of the animation.
@ -49,12 +48,10 @@ namespace Avalonia.Animation
public abstract IObservable<T> DoTransition(IObservable<double> progress, T oldValue, T newValue); public abstract IObservable<T> DoTransition(IObservable<double> progress, T oldValue, T newValue);
/// <inheritdocs/> /// <inheritdocs/>
public virtual IDisposable Apply(Animatable control, object oldValue, object newValue) public virtual IDisposable Apply(Animatable control, IClock clock, object oldValue, object newValue)
{ {
var transition = DoTransition(new TransitionInstance(Duration), (T)oldValue, (T)newValue); var transition = DoTransition(new TransitionInstance(clock, Duration), (T)oldValue, (T)newValue);
return control.Bind<T>((AvaloniaProperty<T>)Property, transition, Data.BindingPriority.Animation); return control.Bind<T>((AvaloniaProperty<T>)Property, transition, Data.BindingPriority.Animation);
} }
} }
} }

1
src/Avalonia.Base/Avalonia.Base.csproj

@ -3,6 +3,7 @@
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Avalonia.Base</AssemblyName> <AssemblyName>Avalonia.Base</AssemblyName>
<RootNamespace>Avalonia</RootNamespace> <RootNamespace>Avalonia</RootNamespace>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<Import Project="..\..\build\Base.props" /> <Import Project="..\..\build\Base.props" />
<Import Project="..\..\build\Binding.props" /> <Import Project="..\..\build\Binding.props" />

18
src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs

@ -0,0 +1,18 @@
using System;
namespace Avalonia.Platform.Interop
{
public interface IDynamicLibraryLoader
{
IntPtr LoadLibrary(string dll);
IntPtr GetProcAddress(IntPtr dll, string proc, bool optional);
}
public class DynamicLibraryLoaderException : Exception
{
public DynamicLibraryLoaderException(string message) : base(message)
{
}
}
}

4
src/Gtk/Avalonia.Gtk3/Interop/Utf8Buffer.cs → src/Avalonia.Base/Platform/Interop/Utf8Buffer.cs

@ -2,9 +2,9 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
namespace Avalonia.Gtk3.Interop namespace Avalonia.Platform.Interop
{ {
class Utf8Buffer : SafeHandle public class Utf8Buffer : SafeHandle
{ {
private GCHandle _gchandle; private GCHandle _gchandle;
private byte[] _data; private byte[] _data;

7
src/Avalonia.Base/PriorityBindingEntry.cs

@ -50,6 +50,11 @@ namespace Avalonia
get; get;
} }
/// <summary>
/// Gets a value indicating whether the binding has completed.
/// </summary>
public bool HasCompleted { get; private set; }
/// <summary> /// <summary>
/// The current value of the binding. /// The current value of the binding.
/// </summary> /// </summary>
@ -129,6 +134,8 @@ namespace Avalonia
private void Completed() private void Completed()
{ {
HasCompleted = true;
if (Dispatcher.UIThread.CheckAccess()) if (Dispatcher.UIThread.CheckAccess())
{ {
_owner.Completed(this); _owner.Completed(this);

14
src/Avalonia.Base/PriorityLevel.cs

@ -112,12 +112,16 @@ namespace Avalonia
return Disposable.Create(() => return Disposable.Create(() =>
{ {
Bindings.Remove(node); if (!entry.HasCompleted)
entry.Dispose();
if (entry.Index >= ActiveBindingIndex)
{ {
ActivateFirstBinding(); Bindings.Remove(node);
entry.Dispose();
if (entry.Index >= ActiveBindingIndex)
{
ActivateFirstBinding();
}
} }
}); });
} }

10
src/Avalonia.Base/Reactive/LightweightObservableBase.cs

@ -82,18 +82,10 @@ namespace Avalonia.Reactive
if (observers.Count == 0) if (observers.Count == 0)
{ {
observers.TrimExcess(); observers.TrimExcess();
Deinitialize();
} }
else
{
return;
}
} else
{
return;
} }
} }
Deinitialize();
} }
} }

5
src/Avalonia.Base/Reactive/ObservableEx.cs

@ -21,7 +21,7 @@ namespace Avalonia.Reactive
{ {
return new SingleValueImpl<T>(value); return new SingleValueImpl<T>(value);
} }
private class SingleValueImpl<T> : IObservable<T> private class SingleValueImpl<T> : IObservable<T>
{ {
private T _value; private T _value;
@ -30,7 +30,6 @@ namespace Avalonia.Reactive
{ {
_value = value; _value = value;
} }
public IDisposable Subscribe(IObserver<T> observer) public IDisposable Subscribe(IObserver<T> observer)
{ {
observer.OnNext(_value); observer.OnNext(_value);
@ -38,4 +37,4 @@ namespace Avalonia.Reactive
} }
} }
} }
} }

2
src/Avalonia.Controls/AppBuilderBase.cs

@ -272,10 +272,10 @@ namespace Avalonia.Controls
s_setupWasAlreadyCalled = true; s_setupWasAlreadyCalled = true;
Instance.RegisterServices();
RuntimePlatformServicesInitializer(); RuntimePlatformServicesInitializer();
WindowingSubsystemInitializer(); WindowingSubsystemInitializer();
RenderingSubsystemInitializer(); RenderingSubsystemInitializer();
Instance.RegisterServices();
Instance.Initialize(); Instance.Initialize();
AfterSetupCallback(Self); AfterSetupCallback(Self);
} }

7
src/Avalonia.Controls/Application.cs

@ -4,12 +4,14 @@
using System; using System;
using System.Reactive.Concurrency; using System.Reactive.Concurrency;
using System.Threading; using System.Threading;
using Avalonia.Animation;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Platform; using Avalonia.Input.Platform;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
@ -335,6 +337,11 @@ namespace Avalonia
.Bind<IScheduler>().ToConstant(AvaloniaScheduler.Instance) .Bind<IScheduler>().ToConstant(AvaloniaScheduler.Instance)
.Bind<IDragDropDevice>().ToConstant(DragDropDevice.Instance) .Bind<IDragDropDevice>().ToConstant(DragDropDevice.Instance)
.Bind<IPlatformDragSource>().ToTransient<InProcessDragSource>(); .Bind<IPlatformDragSource>().ToTransient<InProcessDragSource>();
var clock = new RenderLoopClock();
AvaloniaLocator.CurrentMutable
.Bind<IGlobalClock>().ToConstant(clock)
.GetService<IRenderLoop>()?.Add(clock);
} }
} }
} }

6
src/Avalonia.Controls/Border.cs

@ -43,7 +43,11 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
static Border() static Border()
{ {
AffectsRender<Border>(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty); AffectsRender<Border>(
BackgroundProperty,
BorderBrushProperty,
BorderThicknessProperty,
CornerRadiusProperty);
AffectsMeasure<Border>(BorderThicknessProperty); AffectsMeasure<Border>(BorderThicknessProperty);
} }

2
src/Avalonia.Controls/DrawingPresenter.cs

@ -49,7 +49,7 @@ namespace Avalonia.Controls
if (Drawing != null) if (Drawing != null)
{ {
using (context.PushPreTransform(_transform)) using (context.PushPreTransform(_transform))
using (context.PushClip(Bounds)) using (context.PushClip(new Rect(Bounds.Size)))
{ {
Drawing.Draw(context); Drawing.Draw(context);
} }

4
src/Avalonia.Controls/Generators/ItemContainerGenerator.cs

@ -15,7 +15,7 @@ namespace Avalonia.Controls.Generators
/// </summary> /// </summary>
public class ItemContainerGenerator : IItemContainerGenerator public class ItemContainerGenerator : IItemContainerGenerator
{ {
private Dictionary<int, ItemContainerInfo> _containers = new Dictionary<int, ItemContainerInfo>(); private SortedDictionary<int, ItemContainerInfo> _containers = new SortedDictionary<int, ItemContainerInfo>();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ItemContainerGenerator"/> class. /// Initializes a new instance of the <see cref="ItemContainerGenerator"/> class.
@ -246,4 +246,4 @@ namespace Avalonia.Controls.Generators
Recycled?.Invoke(this, e); Recycled?.Invoke(this, e);
} }
} }
} }

1
src/Avalonia.Controls/Image.cs

@ -26,6 +26,7 @@ namespace Avalonia.Controls
static Image() static Image()
{ {
AffectsRender<Image>(SourceProperty, StretchProperty); AffectsRender<Image>(SourceProperty, StretchProperty);
AffectsMeasure<Image>(SourceProperty, StretchProperty);
} }
/// <summary> /// <summary>

38
src/Avalonia.Controls/MenuItem.cs

@ -99,6 +99,7 @@ namespace Avalonia.Controls
SelectableMixin.Attach<MenuItem>(IsSelectedProperty); SelectableMixin.Attach<MenuItem>(IsSelectedProperty);
CommandProperty.Changed.Subscribe(CommandChanged); CommandProperty.Changed.Subscribe(CommandChanged);
FocusableProperty.OverrideDefaultValue<MenuItem>(true); FocusableProperty.OverrideDefaultValue<MenuItem>(true);
HeaderProperty.Changed.AddClassHandler<MenuItem>(x => x.HeaderChanged);
IconProperty.Changed.AddClassHandler<MenuItem>(x => x.IconChanged); IconProperty.Changed.AddClassHandler<MenuItem>(x => x.IconChanged);
IsSelectedProperty.Changed.AddClassHandler<MenuItem>(x => x.IsSelectedChanged); IsSelectedProperty.Changed.AddClassHandler<MenuItem>(x => x.IsSelectedChanged);
ItemsPanelProperty.OverrideDefaultValue<MenuItem>(DefaultPanel); ItemsPanelProperty.OverrideDefaultValue<MenuItem>(DefaultPanel);
@ -357,10 +358,21 @@ namespace Avalonia.Controls
{ {
base.OnTemplateApplied(e); base.OnTemplateApplied(e);
_popup = e.NameScope.Get<Popup>("PART_Popup"); if (_popup != null)
_popup.DependencyResolver = DependencyResolver.Instance; {
_popup.Opened += PopupOpened; _popup.Opened -= PopupOpened;
_popup.Closed += PopupClosed; _popup.Closed -= PopupClosed;
_popup.DependencyResolver = null;
}
_popup = e.NameScope.Find<Popup>("PART_Popup");
if (_popup != null)
{
_popup.DependencyResolver = DependencyResolver.Instance;
_popup.Opened += PopupOpened;
_popup.Closed += PopupClosed;
}
} }
/// <summary> /// <summary>
@ -408,6 +420,24 @@ namespace Avalonia.Controls
IsEnabled = Command == null || Command.CanExecute(CommandParameter); IsEnabled = Command == null || Command.CanExecute(CommandParameter);
} }
/// <summary>
/// Called when the <see cref="Header"/> property changes.
/// </summary>
/// <param name="e">The property change event.</param>
private void HeaderChanged(AvaloniaPropertyChangedEventArgs e)
{
if (e.NewValue is string newValue && newValue == "-")
{
PseudoClasses.Add(":separator");
Focusable = false;
}
else if (e.OldValue is string oldValue && oldValue == "-")
{
PseudoClasses.Remove(":separator");
Focusable = true;
}
}
/// <summary> /// <summary>
/// Called when the <see cref="Icon"/> property changes. /// Called when the <see cref="Icon"/> property changes.
/// </summary> /// </summary>

1
src/Avalonia.Controls/Panel.cs

@ -30,6 +30,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
static Panel() static Panel()
{ {
AffectsRender<Panel>(BackgroundProperty);
ClipToBoundsProperty.OverrideDefaultValue<Panel>(true); ClipToBoundsProperty.OverrideDefaultValue<Panel>(true);
} }

2
src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs

@ -331,7 +331,7 @@ namespace Avalonia.Controls.Platform
{ {
var item = GetMenuItem(e.Source as IControl); var item = GetMenuItem(e.Source as IControl);
if (e.MouseButton == MouseButton.Left && item.HasSubMenu == false) if (e.MouseButton == MouseButton.Left && item?.HasSubMenu == false)
{ {
Click(item); Click(item);
e.Handled = true; e.Handled = true;

11
src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs

@ -9,12 +9,15 @@ using Avalonia.Threading;
namespace Avalonia.Controls.Platform namespace Avalonia.Controls.Platform
{ {
public class InternalPlatformThreadingInterface : IPlatformThreadingInterface, IRenderLoop public class InternalPlatformThreadingInterface : IPlatformThreadingInterface, IRenderTimer
{ {
public InternalPlatformThreadingInterface() public InternalPlatformThreadingInterface()
{ {
TlsCurrentThreadIsLoopThread = true; TlsCurrentThreadIsLoopThread = true;
StartTimer(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 66), () => Tick?.Invoke(this, new EventArgs())); StartTimer(
DispatcherPriority.Render,
new TimeSpan(0, 0, 0, 0, 66),
() => Tick?.Invoke(TimeSpan.FromMilliseconds(Environment.TickCount)));
} }
private readonly AutoResetEvent _signaled = new AutoResetEvent(false); private readonly AutoResetEvent _signaled = new AutoResetEvent(false);
@ -105,7 +108,7 @@ namespace Avalonia.Controls.Platform
public bool CurrentThreadIsLoopThread => TlsCurrentThreadIsLoopThread; public bool CurrentThreadIsLoopThread => TlsCurrentThreadIsLoopThread;
public event Action<DispatcherPriority?> Signaled; public event Action<DispatcherPriority?> Signaled;
public event EventHandler<EventArgs> Tick; public event Action<TimeSpan> Tick;
} }
} }

8
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@ -197,13 +197,6 @@ namespace Avalonia.Controls.Presenters
} }
} }
/// <inheritdoc/>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
_dataTemplate = null;
}
/// <summary> /// <summary>
/// Updates the <see cref="Child"/> control based on the control's <see cref="Content"/>. /// Updates the <see cref="Child"/> control based on the control's <see cref="Content"/>.
/// </summary> /// </summary>
@ -268,6 +261,7 @@ namespace Avalonia.Controls.Presenters
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{ {
base.OnAttachedToLogicalTree(e); base.OnAttachedToLogicalTree(e);
_dataTemplate = null;
_createdChild = false; _createdChild = false;
InvalidateMeasure(); InvalidateMeasure();
} }

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

@ -6,11 +6,12 @@ using System.Collections.Specialized;
using System.Linq; using System.Linq;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Rendering;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
{ {
// TODO: Need to track position of adorned elements and move the adorner if they move. // TODO: Need to track position of adorned elements and move the adorner if they move.
public class AdornerLayer : Panel public class AdornerLayer : Panel, ICustomSimpleHitTest
{ {
public static AttachedProperty<Visual> AdornedElementProperty = public static AttachedProperty<Visual> AdornedElementProperty =
AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, Visual>("AdornedElement"); AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, Visual>("AdornedElement");
@ -137,6 +138,11 @@ namespace Avalonia.Controls.Primitives
} }
} }
public bool HitTest(Point point)
{
return Children.Any(ctrl => ctrl.TransformedBounds?.Contains(point) == true);
}
private class AdornedElementInfo private class AdornedElementInfo
{ {
public IDisposable Subscription { get; set; } public IDisposable Subscription { get; set; }

8
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -289,12 +289,12 @@ namespace Avalonia.Controls.Primitives
/// <inheritdoc/> /// <inheritdoc/>
public override void EndInit() public override void EndInit()
{ {
base.EndInit();
if (--_updateCount == 0) if (--_updateCount == 0)
{ {
UpdateFinished(); UpdateFinished();
} }
base.EndInit();
} }
/// <summary> /// <summary>
@ -871,8 +871,8 @@ namespace Avalonia.Controls.Primitives
RaisePropertyChanged(SelectedItemProperty, oldItem, item, BindingPriority.LocalValue); RaisePropertyChanged(SelectedItemProperty, oldItem, item, BindingPriority.LocalValue);
} }
added = e.OldItems; added = e.NewItems;
removed = e.NewItems; removed = e.OldItems;
break; break;
} }

7
src/Avalonia.Controls/ProgressBar.cs

@ -37,7 +37,8 @@ namespace Avalonia.Controls
PseudoClass<ProgressBar, Orientation>(OrientationProperty, o => o == Avalonia.Controls.Orientation.Horizontal, ":horizontal"); PseudoClass<ProgressBar, Orientation>(OrientationProperty, o => o == Avalonia.Controls.Orientation.Horizontal, ":horizontal");
PseudoClass<ProgressBar>(IsIndeterminateProperty, ":indeterminate"); PseudoClass<ProgressBar>(IsIndeterminateProperty, ":indeterminate");
ValueProperty.Changed.AddClassHandler<ProgressBar>(x => x.ValueChanged); ValueProperty.Changed.AddClassHandler<ProgressBar>(x => x.UpdateIndicatorWhenPropChanged);
IsIndeterminateProperty.Changed.AddClassHandler<ProgressBar>(x => x.UpdateIndicatorWhenPropChanged);
} }
public bool IsIndeterminate public bool IsIndeterminate
@ -114,9 +115,9 @@ namespace Avalonia.Controls
} }
} }
private void ValueChanged(AvaloniaPropertyChangedEventArgs e) private void UpdateIndicatorWhenPropChanged(AvaloniaPropertyChangedEventArgs e)
{ {
UpdateIndicator(Bounds.Size); UpdateIndicator(Bounds.Size);
} }
} }
} }

4
src/Avalonia.Controls/TextBlock.cs

@ -6,6 +6,7 @@ using System.Reactive;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Metadata; using Avalonia.Metadata;
namespace Avalonia.Controls namespace Avalonia.Controls
@ -65,7 +66,7 @@ namespace Avalonia.Controls
public static readonly AttachedProperty<IBrush> ForegroundProperty = public static readonly AttachedProperty<IBrush> ForegroundProperty =
AvaloniaProperty.RegisterAttached<TextBlock, Control, IBrush>( AvaloniaProperty.RegisterAttached<TextBlock, Control, IBrush>(
nameof(Foreground), nameof(Foreground),
new SolidColorBrush(0xff000000), Brushes.Black,
inherits: true); inherits: true);
/// <summary> /// <summary>
@ -100,6 +101,7 @@ namespace Avalonia.Controls
{ {
ClipToBoundsProperty.OverrideDefaultValue<TextBlock>(true); ClipToBoundsProperty.OverrideDefaultValue<TextBlock>(true);
AffectsRender<TextBlock>( AffectsRender<TextBlock>(
BackgroundProperty,
ForegroundProperty, ForegroundProperty,
FontWeightProperty, FontWeightProperty,
FontSizeProperty, FontSizeProperty,

5
src/Avalonia.Controls/TextBox.cs

@ -212,7 +212,10 @@ namespace Avalonia.Controls
{ {
if (!_ignoreTextChanges) if (!_ignoreTextChanges)
{ {
CaretIndex = CoerceCaretIndex(CaretIndex, value?.Length ?? 0); var caretIndex = CaretIndex;
SelectionStart = CoerceCaretIndex(SelectionStart, value?.Length ?? 0);
SelectionEnd = CoerceCaretIndex(SelectionEnd, value?.Length ?? 0);
CaretIndex = CoerceCaretIndex(caretIndex, value?.Length ?? 0);
if (SetAndRaise(TextProperty, ref _text, value) && !_isUndoingRedoing) if (SetAndRaise(TextProperty, ref _text, value) && !_isUndoingRedoing)
{ {

1
src/Avalonia.Controls/TopLevel.cs

@ -96,7 +96,6 @@ namespace Avalonia.Controls
_applicationLifecycle = TryGetService<IApplicationLifecycle>(dependencyResolver); _applicationLifecycle = TryGetService<IApplicationLifecycle>(dependencyResolver);
_renderInterface = TryGetService<IPlatformRenderInterface>(dependencyResolver); _renderInterface = TryGetService<IPlatformRenderInterface>(dependencyResolver);
var renderLoop = TryGetService<IRenderLoop>(dependencyResolver);
Renderer = impl.CreateRenderer(this); Renderer = impl.CreateRenderer(this);
impl.SetInputRoot(this); impl.SetInputRoot(this);

156
src/Avalonia.Controls/Utils/BorderRenderHelper.cs

@ -88,24 +88,82 @@ namespace Avalonia.Controls.Utils
} }
else else
{ {
var borderThickness = borders.Left; var borderThickness = borders.Top;
var cornerRadius = (float)radii.TopLeft; var top = borderThickness * 0.5;
var rect = new Rect(size); var cornerRadius = (float)Math.Max(0, radii.TopLeft - borderThickness - top);
if (background != null) if (background != null)
{ {
context.FillRectangle(background, rect.Deflate(borders), cornerRadius); var topLeft = new Point(borders.Left, borders.Top);
var bottomRight = new Point(size.Width - borders.Right, size.Height - borders.Bottom);
var innerRect = new Rect(topLeft, bottomRight);
context.FillRectangle(background, innerRect, cornerRadius);
} }
if (borderBrush != null && borderThickness > 0) if (borderBrush != null && borderThickness > 0)
{ {
context.DrawRectangle(new Pen(borderBrush, borderThickness), rect.Deflate(borderThickness), cornerRadius); var topLeft = new Point(top, top);
var bottomRight = new Point(size.Width - top, size.Height - top);
var outerRect = new Rect(topLeft, bottomRight);
context.DrawRectangle(new Pen(borderBrush, borderThickness), outerRect, (float)radii.TopLeft);
} }
} }
}
private static void CreateGeometry(StreamGeometryContext context, Rect boundRect, BorderGeometryKeypoints keypoints)
{
context.BeginFigure(keypoints.TopLeft, true);
// Top
context.LineTo(keypoints.TopRight);
// TopRight corner
var radiusX = boundRect.TopRight.X - keypoints.TopRight.X;
var radiusY = keypoints.RightTop.Y - boundRect.TopRight.Y;
if (radiusX != 0 || radiusY != 0)
{
context.ArcTo(keypoints.RightTop, new Size(radiusY, radiusY), 0, false, SweepDirection.Clockwise);
}
// Right
context.LineTo(keypoints.RightBottom);
// BottomRight corner
radiusX = boundRect.BottomRight.X - keypoints.BottomRight.X;
radiusY = boundRect.BottomRight.Y - keypoints.RightBottom.Y;
if (radiusX != 0 || radiusY != 0)
{
context.ArcTo(keypoints.BottomRight, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise);
}
// Bottom
context.LineTo(keypoints.BottomLeft);
// BottomLeft corner
radiusX = keypoints.BottomLeft.X - boundRect.BottomLeft.X;
radiusY = boundRect.BottomLeft.Y - keypoints.LeftBottom.Y;
if (radiusX != 0 || radiusY != 0)
{
context.ArcTo(keypoints.LeftBottom, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise);
}
// Left
context.LineTo(keypoints.LeftTop);
// TopLeft corner
radiusX = keypoints.TopLeft.X - boundRect.TopLeft.X;
radiusY = keypoints.LeftTop.Y - boundRect.TopLeft.Y;
if (radiusX != 0 || radiusY != 0)
{
context.ArcTo(keypoints.TopLeft, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise);
}
context.EndFigure(true);
} }
private class BorderGeometryKeypoints private class BorderGeometryKeypoints
{ {
internal BorderGeometryKeypoints(Rect boundRect, Thickness borderThickness, CornerRadius cornerRadius, bool inner) internal BorderGeometryKeypoints(Rect boundRect, Thickness borderThickness, CornerRadius cornerRadius, bool inner)
{ {
var left = 0.5 * borderThickness.Left; var left = 0.5 * borderThickness.Left;
@ -135,25 +193,24 @@ namespace Avalonia.Controls.Utils
} }
else else
{ {
leftTopY = cornerRadius.TopLeft + top + boundRect.TopLeft.Y; leftTopY = cornerRadius.TopLeft + top + boundRect.TopLeft.Y;
topLeftX = cornerRadius.TopLeft + left + boundRect.TopLeft.X; topLeftX = cornerRadius.TopLeft + left + boundRect.TopLeft.X;
topRightX = boundRect.Width - (cornerRadius.TopRight + right) + boundRect.TopLeft.X; topRightX = boundRect.Width - (cornerRadius.TopRight + right) + boundRect.TopLeft.X;
rightTopY = cornerRadius.TopRight + top + boundRect.TopLeft.Y; rightTopY = cornerRadius.TopRight + top + boundRect.TopLeft.Y;
rightBottomY = boundRect.Height - (cornerRadius.BottomRight + bottom) + boundRect.TopLeft.Y; rightBottomY = boundRect.Height - (cornerRadius.BottomRight + bottom) + boundRect.TopLeft.Y;
bottomRightX = boundRect.Width - (cornerRadius.BottomRight + right) + boundRect.TopLeft.X; bottomRightX = boundRect.Width - (cornerRadius.BottomRight + right) + boundRect.TopLeft.X;
bottomLeftX = cornerRadius.BottomLeft + left + boundRect.TopLeft.X; bottomLeftX = cornerRadius.BottomLeft + left + boundRect.TopLeft.X;
leftBottomY = boundRect.Height - (cornerRadius.BottomLeft + bottom) + boundRect.TopLeft.Y; leftBottomY = boundRect.Height - (cornerRadius.BottomLeft + bottom) + boundRect.TopLeft.Y;
} }
var leftTopX = boundRect.TopLeft.X; var leftTopX = boundRect.TopLeft.X;
var topLeftY = boundRect.TopLeft.Y; var topLeftY = boundRect.TopLeft.Y;
var topRightY = boundRect.TopLeft.Y; var topRightY = boundRect.TopLeft.Y;
var rightTopX = boundRect.Width + boundRect.TopLeft.X; var rightTopX = boundRect.Width + boundRect.TopLeft.X;
var rightBottomX = boundRect.Width + boundRect.TopLeft.X; var rightBottomX = boundRect.Width + boundRect.TopLeft.X;
var bottomRightY = boundRect.Height + boundRect.TopLeft.Y; var bottomRightY = boundRect.Height + boundRect.TopLeft.Y;
var bottomLeftY = boundRect.Height + boundRect.TopLeft.Y; var bottomLeftY = boundRect.Height + boundRect.TopLeft.Y;
var leftBottomX = boundRect.TopLeft.X; var leftBottomX = boundRect.TopLeft.X;
LeftTop = new Point(leftTopX, leftTopY); LeftTop = new Point(leftTopX, leftTopY);
TopLeft = new Point(topLeftX, topLeftY); TopLeft = new Point(topLeftX, topLeftY);
@ -164,7 +221,7 @@ namespace Avalonia.Controls.Utils
BottomLeft = new Point(bottomLeftX, bottomLeftY); BottomLeft = new Point(bottomLeftX, bottomLeftY);
LeftBottom = new Point(leftBottomX, leftBottomY); LeftBottom = new Point(leftBottomX, leftBottomY);
//Fix overlap // Fix overlap
if (TopLeft.X > TopRight.X) if (TopLeft.X > TopRight.X)
{ {
var scaledX = topLeftX / (topLeftX + topRightX) * boundRect.Width; var scaledX = topLeftX / (topLeftX + topRightX) * boundRect.Width;
@ -194,66 +251,21 @@ namespace Avalonia.Controls.Utils
} }
} }
internal Point LeftTop { get; private set; } internal Point LeftTop { get; }
internal Point TopLeft { get; private set; }
internal Point TopRight { get; private set; }
internal Point RightTop { get; private set; }
internal Point RightBottom { get; private set; }
internal Point BottomRight { get; private set; }
internal Point BottomLeft { get; private set; }
internal Point LeftBottom { get; private set; }
}
private static void CreateGeometry(StreamGeometryContext context, Rect boundRect, BorderGeometryKeypoints keypoints) internal Point TopLeft { get; }
{
context.BeginFigure(keypoints.TopLeft, true);
//Top internal Point TopRight { get; }
context.LineTo(keypoints.TopRight);
//TopRight corner internal Point RightTop { get; }
var radiusX = boundRect.TopRight.X - keypoints.TopRight.X;
var radiusY = keypoints.RightTop.Y - boundRect.TopRight.Y;
if (radiusX != 0 || radiusY != 0)
{
context.ArcTo(keypoints.RightTop, new Size(radiusY, radiusY), 0, false, SweepDirection.Clockwise);
}
//Right internal Point RightBottom { get; }
context.LineTo(keypoints.RightBottom);
//BottomRight corner internal Point BottomRight { get; }
radiusX = boundRect.BottomRight.X - keypoints.BottomRight.X;
radiusY = boundRect.BottomRight.Y - keypoints.RightBottom.Y;
if (radiusX != 0 || radiusY != 0)
{
context.ArcTo(keypoints.BottomRight, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise);
}
//Bottom
context.LineTo(keypoints.BottomLeft);
//BottomLeft corner
radiusX = keypoints.BottomLeft.X - boundRect.BottomLeft.X;
radiusY = boundRect.BottomLeft.Y - keypoints.LeftBottom.Y;
if (radiusX != 0 || radiusY != 0)
{
context.ArcTo(keypoints.LeftBottom, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise);
}
//Left internal Point BottomLeft { get; }
context.LineTo(keypoints.LeftTop);
//TopLeft corner
radiusX = keypoints.TopLeft.X - boundRect.TopLeft.X;
radiusY = keypoints.LeftTop.Y - boundRect.TopLeft.Y;
if (radiusX != 0 || radiusY != 0)
{
context.ArcTo(keypoints.TopLeft, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise);
}
context.EndFigure(true); internal Point LeftBottom { get; }
} }
} }
} }

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

@ -53,7 +53,8 @@ namespace Avalonia.DesignerSupport.Remote
.Bind<IKeyboardDevice>().ToConstant(Keyboard) .Bind<IKeyboardDevice>().ToConstant(Keyboard)
.Bind<IPlatformSettings>().ToConstant(instance) .Bind<IPlatformSettings>().ToConstant(instance)
.Bind<IPlatformThreadingInterface>().ToConstant(threading) .Bind<IPlatformThreadingInterface>().ToConstant(threading)
.Bind<IRenderLoop>().ToConstant(threading) .Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<IRenderTimer>().ToConstant(threading)
.Bind<ISystemDialogImpl>().ToSingleton<SystemDialogsStub>() .Bind<ISystemDialogImpl>().ToSingleton<SystemDialogsStub>()
.Bind<IWindowingPlatform>().ToConstant(instance) .Bind<IWindowingPlatform>().ToConstant(instance)
.Bind<IPlatformIconLoader>().ToSingleton<IconLoaderStub>(); .Bind<IPlatformIconLoader>().ToSingleton<IconLoaderStub>();

8
src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj

@ -2,6 +2,9 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove="Views\EventsView.xaml" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" /> <ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
<ProjectReference Include="..\Markup\Avalonia.Markup\Avalonia.Markup.csproj" /> <ProjectReference Include="..\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
@ -17,4 +20,9 @@
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\EmbedXaml.props" /> <Import Project="..\..\build\EmbedXaml.props" />
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<ItemGroup>
<EmbeddedResource Update="Views\EventsView.xaml">
<Generator>MSBuild:Compile</Generator>
</EmbeddedResource>
</ItemGroup>
</Project> </Project>

1
src/Avalonia.Diagnostics/DevTools.xaml

@ -3,6 +3,7 @@
<TabStrip SelectedIndex="{Binding SelectedTab, Mode=TwoWay}"> <TabStrip SelectedIndex="{Binding SelectedTab, Mode=TwoWay}">
<TabStripItem Content="Logical Tree"/> <TabStripItem Content="Logical Tree"/>
<TabStripItem Content="Visual Tree"/> <TabStripItem Content="Visual Tree"/>
<TabStripItem Content="Events"/>
</TabStrip> </TabStrip>
<ContentControl Content="{Binding Content}" Grid.Row="1"/> <ContentControl Content="{Binding Content}" Grid.Row="1"/>

23
src/Avalonia.Diagnostics/DevTools.xaml.cs

@ -10,6 +10,7 @@ using Avalonia.Input;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Rendering;
using Avalonia.VisualTree; using Avalonia.VisualTree;
namespace Avalonia namespace Avalonia
@ -28,6 +29,7 @@ namespace Avalonia.Diagnostics
public class DevTools : UserControl public class DevTools : UserControl
{ {
private static Dictionary<TopLevel, Window> s_open = new Dictionary<TopLevel, Window>(); private static Dictionary<TopLevel, Window> s_open = new Dictionary<TopLevel, Window>();
private static HashSet<IRenderRoot> s_visualTreeRoots = new HashSet<IRenderRoot>();
private IDisposable _keySubscription; private IDisposable _keySubscription;
public DevTools(IControl root) public DevTools(IControl root)
@ -79,6 +81,7 @@ namespace Avalonia.Diagnostics
devToolsWindow.Closed += devTools.DevToolsClosed; devToolsWindow.Closed += devTools.DevToolsClosed;
s_open.Add(control, devToolsWindow); s_open.Add(control, devToolsWindow);
MarkAsDevTool(devToolsWindow);
devToolsWindow.Show(); devToolsWindow.Show();
} }
} }
@ -89,6 +92,7 @@ namespace Avalonia.Diagnostics
var devToolsWindow = (Window)sender; var devToolsWindow = (Window)sender;
var devTools = (DevTools)devToolsWindow.Content; var devTools = (DevTools)devToolsWindow.Content;
s_open.Remove((TopLevel)devTools.Root); s_open.Remove((TopLevel)devTools.Root);
RemoveDevTool(devToolsWindow);
_keySubscription.Dispose(); _keySubscription.Dispose();
devToolsWindow.Closed -= DevToolsClosed; devToolsWindow.Closed -= DevToolsClosed;
} }
@ -116,5 +120,24 @@ namespace Avalonia.Diagnostics
} }
} }
} }
/// <summary>
/// Marks a visual as part of the DevTools, so it can be excluded from event tracking.
/// </summary>
/// <param name="visual">The visual whose root is to be marked.</param>
public static void MarkAsDevTool(IVisual visual)
{
s_visualTreeRoots.Add(visual.GetVisualRoot());
}
public static void RemoveDevTool(IVisual visual)
{
s_visualTreeRoots.Remove(visual.GetVisualRoot());
}
public static bool BelongsToDevTool(IVisual visual)
{
return s_visualTreeRoots.Contains(visual.GetVisualRoot());
}
} }
} }

38
src/Avalonia.Diagnostics/Models/EventChainLink.cs

@ -0,0 +1,38 @@
// 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 Avalonia.Interactivity;
namespace Avalonia.Diagnostics.Models
{
internal class EventChainLink
{
public EventChainLink(object handler, bool handled, RoutingStrategies route)
{
Contract.Requires<ArgumentNullException>(handler != null);
this.Handler = handler;
this.Handled = handled;
this.Route = route;
}
public object Handler { get; }
public string HandlerName
{
get
{
if (Handler is INamed named && !string.IsNullOrEmpty(named.Name))
{
return named.Name + " (" + Handler.GetType().Name + ")";
}
return Handler.GetType().Name;
}
}
public bool Handled { get; }
public RoutingStrategies Route { get; }
}
}

5
src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs

@ -14,6 +14,7 @@ namespace Avalonia.Diagnostics.ViewModels
private int _selectedTab; private int _selectedTab;
private TreePageViewModel _logicalTree; private TreePageViewModel _logicalTree;
private TreePageViewModel _visualTree; private TreePageViewModel _visualTree;
private EventsViewModel _eventsView;
private string _focusedControl; private string _focusedControl;
private string _pointerOverElement; private string _pointerOverElement;
@ -21,6 +22,7 @@ namespace Avalonia.Diagnostics.ViewModels
{ {
_logicalTree = new TreePageViewModel(LogicalTreeNode.Create(root)); _logicalTree = new TreePageViewModel(LogicalTreeNode.Create(root));
_visualTree = new TreePageViewModel(VisualTreeNode.Create(root)); _visualTree = new TreePageViewModel(VisualTreeNode.Create(root));
_eventsView = new EventsViewModel(root);
UpdateFocusedControl(); UpdateFocusedControl();
KeyboardDevice.Instance.PropertyChanged += (s, e) => KeyboardDevice.Instance.PropertyChanged += (s, e) =>
@ -57,6 +59,9 @@ namespace Avalonia.Diagnostics.ViewModels
case 1: case 1:
Content = _visualTree; Content = _visualTree;
break; break;
case 2:
Content = _eventsView;
break;
} }
RaisePropertyChanged(); RaisePropertyChanged();

61
src/Avalonia.Diagnostics/ViewModels/EventOwnerTreeNode.cs

@ -0,0 +1,61 @@
// 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.Collections.Generic;
using System.Linq;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
namespace Avalonia.Diagnostics.ViewModels
{
internal class EventOwnerTreeNode : EventTreeNodeBase
{
private static readonly RoutedEvent[] s_defaultEvents = new RoutedEvent[]
{
Button.ClickEvent,
InputElement.KeyDownEvent,
InputElement.KeyUpEvent,
InputElement.TextInputEvent,
InputElement.PointerReleasedEvent,
InputElement.PointerPressedEvent,
};
public EventOwnerTreeNode(Type type, IEnumerable<RoutedEvent> events, EventsViewModel vm)
: base(null, type.Name)
{
this.Children = new AvaloniaList<EventTreeNodeBase>(events.OrderBy(e => e.Name)
.Select(e => new EventTreeNode(this, e, vm) { IsEnabled = s_defaultEvents.Contains(e) }));
this.IsExpanded = true;
}
public override bool? IsEnabled
{
get => base.IsEnabled;
set
{
if (base.IsEnabled != value)
{
base.IsEnabled = value;
if (_updateChildren && value != null)
{
foreach (var child in Children)
{
try
{
child._updateParent = false;
child.IsEnabled = value;
}
finally
{
child._updateParent = true;
}
}
}
}
}
}
}
}

98
src/Avalonia.Diagnostics/ViewModels/EventTreeNode.cs

@ -0,0 +1,98 @@
// 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 Avalonia.Diagnostics.Models;
using Avalonia.Interactivity;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace Avalonia.Diagnostics.ViewModels
{
internal class EventTreeNode : EventTreeNodeBase
{
private RoutedEvent _event;
private EventsViewModel _parentViewModel;
private bool _isRegistered;
private FiredEvent _currentEvent;
public EventTreeNode(EventOwnerTreeNode parent, RoutedEvent @event, EventsViewModel vm)
: base(parent, @event.Name)
{
Contract.Requires<ArgumentNullException>(@event != null);
Contract.Requires<ArgumentNullException>(vm != null);
this._event = @event;
this._parentViewModel = vm;
}
public override bool? IsEnabled
{
get => base.IsEnabled;
set
{
if (base.IsEnabled != value)
{
base.IsEnabled = value;
UpdateTracker();
if (Parent != null && _updateParent)
{
try
{
Parent._updateChildren = false;
Parent.UpdateChecked();
}
finally
{
Parent._updateChildren = true;
}
}
}
}
}
private void UpdateTracker()
{
if (IsEnabled.GetValueOrDefault() && !_isRegistered)
{
_event.AddClassHandler(typeof(object), HandleEvent, (RoutingStrategies)7, handledEventsToo: true);
_isRegistered = true;
}
}
private void HandleEvent(object sender, RoutedEventArgs e)
{
if (!_isRegistered || IsEnabled == false)
return;
if (sender is IVisual v && DevTools.BelongsToDevTool(v))
return;
var s = sender;
var handled = e.Handled;
var route = e.Route;
Action handler = delegate
{
if (_currentEvent == null || !_currentEvent.IsPartOfSameEventChain(e))
{
_currentEvent = new FiredEvent(e, new EventChainLink(s, handled, route));
_parentViewModel.RecordedEvents.Add(_currentEvent);
while (_parentViewModel.RecordedEvents.Count > 100)
_parentViewModel.RecordedEvents.RemoveAt(0);
}
else
{
_currentEvent.AddToChain(new EventChainLink(s, handled, route));
}
};
if (!Dispatcher.UIThread.CheckAccess())
Dispatcher.UIThread.Post(handler);
else
handler();
}
}
}

78
src/Avalonia.Diagnostics/ViewModels/EventTreeNodeBase.cs

@ -0,0 +1,78 @@
// 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 Avalonia.Collections;
namespace Avalonia.Diagnostics.ViewModels
{
internal abstract class EventTreeNodeBase : ViewModelBase
{
internal bool _updateChildren = true;
internal bool _updateParent = true;
private bool _isExpanded;
private bool? _isEnabled = false;
public EventTreeNodeBase(EventTreeNodeBase parent, string text)
{
this.Parent = parent;
this.Text = text;
}
public IAvaloniaReadOnlyList<EventTreeNodeBase> Children
{
get;
protected set;
}
public bool IsExpanded
{
get { return _isExpanded; }
set { RaiseAndSetIfChanged(ref _isExpanded, value); }
}
public virtual bool? IsEnabled
{
get { return _isEnabled; }
set { RaiseAndSetIfChanged(ref _isEnabled, value); }
}
public EventTreeNodeBase Parent
{
get;
}
public string Text
{
get;
private set;
}
internal void UpdateChecked()
{
IsEnabled = GetValue();
bool? GetValue()
{
if (Children == null)
return false;
bool? value = false;
for (int i = 0; i < Children.Count; i++)
{
if (i == 0)
{
value = Children[i].IsEnabled;
continue;
}
if (value != Children[i].IsEnabled)
{
value = null;
break;
}
}
return value;
}
}
}
}

60
src/Avalonia.Diagnostics/ViewModels/EventsViewModel.cs

@ -0,0 +1,60 @@
// 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.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Windows.Input;
using Avalonia.Controls;
using Avalonia.Data.Converters;
using Avalonia.Interactivity;
using Avalonia.Media;
namespace Avalonia.Diagnostics.ViewModels
{
internal class EventsViewModel : ViewModelBase
{
private readonly IControl _root;
private FiredEvent _selectedEvent;
public EventsViewModel(IControl root)
{
this._root = root;
this.Nodes = RoutedEventRegistry.Instance.GetAllRegistered()
.GroupBy(e => e.OwnerType)
.OrderBy(e => e.Key.Name)
.Select(g => new EventOwnerTreeNode(g.Key, g, this))
.ToArray();
}
public EventTreeNodeBase[] Nodes { get; }
public ObservableCollection<FiredEvent> RecordedEvents { get; } = new ObservableCollection<FiredEvent>();
public FiredEvent SelectedEvent
{
get => _selectedEvent;
set => RaiseAndSetIfChanged(ref _selectedEvent, value);
}
private void Clear()
{
RecordedEvents.Clear();
}
}
internal class BoolToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? Brushes.LightGreen : Brushes.Transparent;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

80
src/Avalonia.Diagnostics/ViewModels/FiredEvent.cs

@ -0,0 +1,80 @@
// 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.Collections.ObjectModel;
using Avalonia.Diagnostics.Models;
using Avalonia.Interactivity;
namespace Avalonia.Diagnostics.ViewModels
{
internal class FiredEvent : ViewModelBase
{
private RoutedEventArgs _eventArgs;
private EventChainLink _handledBy;
public FiredEvent(RoutedEventArgs eventArgs, EventChainLink originator)
{
Contract.Requires<ArgumentNullException>(eventArgs != null);
Contract.Requires<ArgumentNullException>(originator != null);
this._eventArgs = eventArgs;
this.Originator = originator;
AddToChain(originator);
}
public bool IsPartOfSameEventChain(RoutedEventArgs e)
{
return e == _eventArgs;
}
public RoutedEvent Event => _eventArgs.RoutedEvent;
public bool IsHandled => HandledBy?.Handled == true;
public ObservableCollection<EventChainLink> EventChain { get; } = new ObservableCollection<EventChainLink>();
public string DisplayText
{
get
{
if (IsHandled)
{
return $"{Event.Name} on {Originator.HandlerName};" + Environment.NewLine +
$"strategies: {Event.RoutingStrategies}; handled by: {HandledBy.HandlerName}";
}
return $"{Event.Name} on {Originator.HandlerName}; strategies: {Event.RoutingStrategies}";
}
}
public EventChainLink Originator { get; }
public EventChainLink HandledBy
{
get { return _handledBy; }
set
{
if (_handledBy != value)
{
_handledBy = value;
RaisePropertyChanged();
RaisePropertyChanged(nameof(IsHandled));
RaisePropertyChanged(nameof(DisplayText));
}
}
}
public void AddToChain(object handler, bool handled, RoutingStrategies route)
{
AddToChain(new EventChainLink(handler, handled, route));
}
public void AddToChain(EventChainLink link)
{
EventChain.Add(link);
if (HandledBy == null && link.Handled)
HandledBy = link;
}
}
}

53
src/Avalonia.Diagnostics/Views/EventsView.xaml

@ -0,0 +1,53 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Avalonia.Diagnostics.ViewModels">
<UserControl.Resources>
<vm:BoolToBrushConverter x:Key="boolToBrush" />
</UserControl.Resources>
<Grid ColumnDefinitions="*,4,3*">
<TreeView Name="tree" Items="{Binding Nodes}" SelectedItem="{Binding SelectedNode, Mode=TwoWay}" Grid.RowSpan="2">
<TreeView.DataTemplates>
<TreeDataTemplate DataType="vm:EventTreeNodeBase"
ItemsSource="{Binding Children}">
<CheckBox Content="{Binding Text}" IsChecked="{Binding IsEnabled, Mode=TwoWay}" />
</TreeDataTemplate>
</TreeView.DataTemplates>
<TreeView.Styles>
<Style Selector="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
</Style>
</TreeView.Styles>
</TreeView>
<GridSplitter Width="4" Grid.Column="1" />
<Grid RowDefinitions="*,4,2*,Auto" Grid.Column="2">
<ListBox Name="eventsList" Items="{Binding RecordedEvents}" SelectedItem="{Binding SelectedEvent, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Background="{Binding IsHandled, Converter={StaticResource boolToBrush}}" Text="{Binding DisplayText}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<GridSplitter Height="4" Grid.Row="1" />
<DockPanel Grid.Row="2" LastChildFill="True">
<TextBlock DockPanel.Dock="Top" FontSize="16" Text="Event chain:" />
<ListBox Items="{Binding SelectedEvent.EventChain}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Background="{Binding Handled, Converter={StaticResource boolToBrush}}">
<TextBlock Text="{Binding Route}" />
<TextBlock Text=": " />
<TextBlock Text="{Binding HandlerName}" />
<TextBlock Text=" handled: " />
<TextBlock Text="{Binding Handled}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
<StackPanel Orientation="Horizontal" Grid.Row="3">
<Button Content="Clear" Margin="3" Command="{Binding Clear}" />
</StackPanel>
</Grid>
</Grid>
</UserControl>

32
src/Avalonia.Diagnostics/Views/EventsView.xaml.cs

@ -0,0 +1,32 @@
// 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.Linq;
using Avalonia.Controls;
using Avalonia.Diagnostics.ViewModels;
using Avalonia.Markup.Xaml;
namespace Avalonia.Diagnostics.Views
{
public class EventsView : UserControl
{
private ListBox _events;
public EventsView()
{
this.InitializeComponent();
_events = this.FindControl<ListBox>("events");
}
private void RecordedEvents_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
_events.ScrollIntoView(_events.Items.OfType<FiredEvent>().LastOrDefault());
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

12
src/Avalonia.OpenGL/Avalonia.OpenGL.csproj

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
</ItemGroup>
</Project>

196
src/Avalonia.OpenGL/EglConsts.cs

@ -0,0 +1,196 @@
// ReSharper disable UnusedMember.Global
// ReSharper disable IdentifierTypo
namespace Avalonia.OpenGL
{
public static class EglConsts
{
public const int EGL_ALPHA_SIZE = 0x3021;
public const int EGL_BAD_ACCESS = 0x3002;
public const int EGL_BAD_ALLOC = 0x3003;
public const int EGL_BAD_ATTRIBUTE = 0x3004;
public const int EGL_BAD_CONFIG = 0x3005;
public const int EGL_BAD_CONTEXT = 0x3006;
public const int EGL_BAD_CURRENT_SURFACE = 0x3007;
public const int EGL_BAD_DISPLAY = 0x3008;
public const int EGL_BAD_MATCH = 0x3009;
public const int EGL_BAD_NATIVE_PIXMAP = 0x300A;
public const int EGL_BAD_NATIVE_WINDOW = 0x300B;
public const int EGL_BAD_PARAMETER = 0x300C;
public const int EGL_BAD_SURFACE = 0x300D;
public const int EGL_BLUE_SIZE = 0x3022;
public const int EGL_BUFFER_SIZE = 0x3020;
public const int EGL_CONFIG_CAVEAT = 0x3027;
public const int EGL_CONFIG_ID = 0x3028;
public const int EGL_CORE_NATIVE_ENGINE = 0x305B;
public const int EGL_DEPTH_SIZE = 0x3025;
public const int EGL_DONT_CARE = -1;
public const int EGL_DRAW = 0x3059;
public const int EGL_EXTENSIONS = 0x3055;
public const int EGL_FALSE = 0;
public const int EGL_GREEN_SIZE = 0x3023;
public const int EGL_HEIGHT = 0x3056;
public const int EGL_LARGEST_PBUFFER = 0x3058;
public const int EGL_LEVEL = 0x3029;
public const int EGL_MAX_PBUFFER_HEIGHT = 0x302A;
public const int EGL_MAX_PBUFFER_PIXELS = 0x302B;
public const int EGL_MAX_PBUFFER_WIDTH = 0x302C;
public const int EGL_NATIVE_RENDERABLE = 0x302D;
public const int EGL_NATIVE_VISUAL_ID = 0x302E;
public const int EGL_NATIVE_VISUAL_TYPE = 0x302F;
public const int EGL_NONE = 0x3038;
public const int EGL_NON_CONFORMANT_CONFIG = 0x3051;
public const int EGL_NOT_INITIALIZED = 0x3001;
public const int EGL_NO_CONTEXT = 0;
public const int EGL_NO_DISPLAY = 0;
public const int EGL_NO_SURFACE = 0;
public const int EGL_PBUFFER_BIT = 0x0001;
public const int EGL_PIXMAP_BIT = 0x0002;
public const int EGL_READ = 0x305A;
public const int EGL_RED_SIZE = 0x3024;
public const int EGL_SAMPLES = 0x3031;
public const int EGL_SAMPLE_BUFFERS = 0x3032;
public const int EGL_SLOW_CONFIG = 0x3050;
public const int EGL_STENCIL_SIZE = 0x3026;
public const int EGL_SUCCESS = 0x3000;
public const int EGL_SURFACE_TYPE = 0x3033;
public const int EGL_TRANSPARENT_BLUE_VALUE = 0x3035;
public const int EGL_TRANSPARENT_GREEN_VALUE = 0x3036;
public const int EGL_TRANSPARENT_RED_VALUE = 0x3037;
public const int EGL_TRANSPARENT_RGB = 0x3052;
public const int EGL_TRANSPARENT_TYPE = 0x3034;
public const int EGL_TRUE = 1;
public const int EGL_VENDOR = 0x3053;
public const int EGL_VERSION = 0x3054;
public const int EGL_WIDTH = 0x3057;
public const int EGL_WINDOW_BIT = 0x0004;
public const int EGL_BACK_BUFFER = 0x3084;
public const int EGL_BIND_TO_TEXTURE_RGB = 0x3039;
public const int EGL_BIND_TO_TEXTURE_RGBA = 0x303A;
public const int EGL_CONTEXT_LOST = 0x300E;
public const int EGL_MIN_SWAP_INTERVAL = 0x303B;
public const int EGL_MAX_SWAP_INTERVAL = 0x303C;
public const int EGL_MIPMAP_TEXTURE = 0x3082;
public const int EGL_MIPMAP_LEVEL = 0x3083;
public const int EGL_NO_TEXTURE = 0x305C;
public const int EGL_TEXTURE_2D = 0x305F;
public const int EGL_TEXTURE_FORMAT = 0x3080;
public const int EGL_TEXTURE_RGB = 0x305D;
public const int EGL_TEXTURE_RGBA = 0x305E;
public const int EGL_TEXTURE_TARGET = 0x3081;
public const int EGL_ALPHA_FORMAT = 0x3088;
public const int EGL_ALPHA_FORMAT_NONPRE = 0x308B;
public const int EGL_ALPHA_FORMAT_PRE = 0x308C;
public const int EGL_ALPHA_MASK_SIZE = 0x303E;
public const int EGL_BUFFER_PRESERVED = 0x3094;
public const int EGL_BUFFER_DESTROYED = 0x3095;
public const int EGL_CLIENT_APIS = 0x308D;
public const int EGL_COLORSPACE = 0x3087;
public const int EGL_COLORSPACE_sRGB = 0x3089;
public const int EGL_COLORSPACE_LINEAR = 0x308A;
public const int EGL_COLOR_BUFFER_TYPE = 0x303F;
public const int EGL_CONTEXT_CLIENT_TYPE = 0x3097;
public const int EGL_DISPLAY_SCALING = 10000;
public const int EGL_HORIZONTAL_RESOLUTION = 0x3090;
public const int EGL_LUMINANCE_BUFFER = 0x308F;
public const int EGL_LUMINANCE_SIZE = 0x303D;
public const int EGL_OPENGL_ES_BIT = 0x0001;
public const int EGL_OPENVG_BIT = 0x0002;
public const int EGL_OPENGL_ES_API = 0x30A0;
public const int EGL_OPENVG_API = 0x30A1;
public const int EGL_OPENVG_IMAGE = 0x3096;
public const int EGL_PIXEL_ASPECT_RATIO = 0x3092;
public const int EGL_RENDERABLE_TYPE = 0x3040;
public const int EGL_RENDER_BUFFER = 0x3086;
public const int EGL_RGB_BUFFER = 0x308E;
public const int EGL_SINGLE_BUFFER = 0x3085;
public const int EGL_SWAP_BEHAVIOR = 0x3093;
public const int EGL_UNKNOWN = -1;
public const int EGL_VERTICAL_RESOLUTION = 0x3091;
public const int EGL_CONFORMANT = 0x3042;
public const int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
public const int EGL_MATCH_NATIVE_PIXMAP = 0x3041;
public const int EGL_OPENGL_ES2_BIT = 0x0004;
public const int EGL_VG_ALPHA_FORMAT = 0x3088;
public const int EGL_VG_ALPHA_FORMAT_NONPRE = 0x308B;
public const int EGL_VG_ALPHA_FORMAT_PRE = 0x308C;
public const int EGL_VG_ALPHA_FORMAT_PRE_BIT = 0x0040;
public const int EGL_VG_COLORSPACE = 0x3087;
public const int EGL_VG_COLORSPACE_sRGB = 0x3089;
public const int EGL_VG_COLORSPACE_LINEAR = 0x308A;
public const int EGL_VG_COLORSPACE_LINEAR_BIT = 0x0020;
public const int EGL_DEFAULT_DISPLAY = 0;
public const int EGL_MULTISAMPLE_RESOLVE_BOX_BIT = 0x0200;
public const int EGL_MULTISAMPLE_RESOLVE = 0x3099;
public const int EGL_MULTISAMPLE_RESOLVE_DEFAULT = 0x309A;
public const int EGL_MULTISAMPLE_RESOLVE_BOX = 0x309B;
public const int EGL_OPENGL_API = 0x30A2;
public const int EGL_OPENGL_BIT = 0x0008;
public const int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
public const int EGL_CONTEXT_MAJOR_VERSION = 0x3098;
public const int EGL_CONTEXT_MINOR_VERSION = 0x30FB;
public const int EGL_CONTEXT_OPENGL_PROFILE_MASK = 0x30FD;
public const int EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY = 0x31BD;
public const int EGL_NO_RESET_NOTIFICATION = 0x31BE;
public const int EGL_LOSE_CONTEXT_ON_RESET = 0x31BF;
public const int EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT = 0x00000001;
public const int EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT = 0x00000002;
public const int EGL_CONTEXT_OPENGL_DEBUG = 0x31B0;
public const int EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE = 0x31B1;
public const int EGL_CONTEXT_OPENGL_ROBUST_ACCESS = 0x31B2;
public const int EGL_OPENGL_ES3_BIT = 0x00000040;
public const int EGL_CL_EVENT_HANDLE = 0x309C;
public const int EGL_SYNC_CL_EVENT = 0x30FE;
public const int EGL_SYNC_CL_EVENT_COMPLETE = 0x30FF;
public const int EGL_SYNC_PRIOR_COMMANDS_COMPLETE = 0x30F0;
public const int EGL_SYNC_TYPE = 0x30F7;
public const int EGL_SYNC_STATUS = 0x30F1;
public const int EGL_SYNC_CONDITION = 0x30F8;
public const int EGL_SIGNALED = 0x30F2;
public const int EGL_UNSIGNALED = 0x30F3;
public const int EGL_SYNC_FLUSH_COMMANDS_BIT = 0x0001;
public const int EGL_TIMEOUT_EXPIRED = 0x30F5;
public const int EGL_CONDITION_SATISFIED = 0x30F6;
public const int EGL_NO_SYNC = 0;
public const int EGL_SYNC_FENCE = 0x30F9;
public const int EGL_GL_COLORSPACE = 0x309D;
public const int EGL_GL_COLORSPACE_SRGB = 0x3089;
public const int EGL_GL_COLORSPACE_LINEAR = 0x308A;
public const int EGL_GL_RENDERBUFFER = 0x30B9;
public const int EGL_GL_TEXTURE_2D = 0x30B1;
public const int EGL_GL_TEXTURE_LEVEL = 0x30BC;
public const int EGL_GL_TEXTURE_3D = 0x30B2;
public const int EGL_GL_TEXTURE_ZOFFSET = 0x30BD;
public const int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x30B3;
public const int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 0x30B4;
public const int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 0x30B5;
public const int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x30B6;
public const int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x30B7;
public const int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x30B8;
public const int EGL_IMAGE_PRESERVED = 0x30D2;
public const int EGL_NO_IMAGE = 0;
public const int EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE = 0x3207;
public const int EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE = 0x3208;
//EGL_ANGLE_platform_angle
public const int EGL_PLATFORM_ANGLE_ANGLE = 0x3202;
public const int EGL_PLATFORM_ANGLE_TYPE_ANGLE = 0x3203;
public const int EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE = 0x3204;
public const int EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE = 0x3205;
public const int EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED = 0x3451;
public const int EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE = 0x3206;
public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE = 0x320A;
public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE = 0x345E;
//EGL_ANGLE_platform_angle_d3d
public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE = 0x3209;
public const int EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE = 0x320F;
public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE = 0x320B;
public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_REFERENCE_ANGLE = 0x320C;
}
}

205
src/Avalonia.OpenGL/EglDisplay.cs

@ -0,0 +1,205 @@
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using Avalonia.Platform.Interop;
using static Avalonia.OpenGL.EglConsts;
namespace Avalonia.OpenGL
{
public class EglDisplay : IGlDisplay
{
private readonly EglInterface _egl;
private readonly IntPtr _display;
private readonly IntPtr _config;
private readonly int[] _contextAttributes;
public EglDisplay(EglInterface egl)
{
_egl = egl;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && _egl.GetPlatformDisplayEXT != null)
{
foreach (var dapi in new[] {EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE})
{
_display = _egl.GetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, IntPtr.Zero, new[]
{
EGL_PLATFORM_ANGLE_TYPE_ANGLE, dapi, EGL_NONE
});
if(_display != IntPtr.Zero)
break;
}
}
if (_display == IntPtr.Zero)
_display = _egl.GetDisplay(IntPtr.Zero);
if(_display == IntPtr.Zero)
throw new OpenGlException("eglGetDisplay failed");
if (!_egl.Initialize(_display, out var major, out var minor))
throw new OpenGlException("eglInitialize failed");
foreach (var cfg in new[]
{
new
{
Attributes = new[] {EGL_NONE},
Api = EGL_OPENGL_API,
RenderableTypeBit = EGL_OPENGL_BIT,
Type = GlDisplayType.OpenGL2
},
new
{
Attributes = new[]
{
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
},
Api = EGL_OPENGL_ES_API,
RenderableTypeBit = EGL_OPENGL_ES2_BIT,
Type = GlDisplayType.OpenGLES2
}
})
{
if (!_egl.BindApi(cfg.Api))
continue;
var attribs = new[]
{
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE, cfg.RenderableTypeBit,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_STENCIL_SIZE, 8,
EGL_DEPTH_SIZE, 8,
EGL_NONE
};
if (!_egl.ChooseConfig(_display, attribs, out _config, 1, out int numConfigs))
continue;
if (numConfigs == 0)
continue;
_contextAttributes = cfg.Attributes;
Type = cfg.Type;
}
if (_contextAttributes == null)
throw new OpenGlException("No suitable EGL config was found");
GlInterface = new GlInterface((proc, optional) =>
{
using (var u = new Utf8Buffer(proc))
{
var rv = _egl.GetProcAddress(u);
if (rv == IntPtr.Zero && !optional)
throw new OpenGlException("Missing function " + proc);
return rv;
}
});
}
public EglDisplay() : this(new EglInterface())
{
}
public GlDisplayType Type { get; }
public GlInterface GlInterface { get; }
public IGlContext CreateContext(IGlContext share)
{
var shareCtx = (EglContext)share;
var ctx = _egl.CreateContext(_display, _config, shareCtx?.Context ?? IntPtr.Zero, _contextAttributes);
if (ctx == IntPtr.Zero)
throw new OpenGlException("eglCreateContext failed");
var surf = _egl.CreatePBufferSurface(_display, _config, new[]
{
EGL_WIDTH, 1,
EGL_HEIGHT, 1,
EGL_NONE
});
if (surf == IntPtr.Zero)
throw new OpenGlException("eglCreatePbufferSurface failed");
var rv = new EglContext(this, ctx, surf);
rv.MakeCurrent(null);
return rv;
}
public void ClearContext()
{
if (!_egl.MakeCurrent(_display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero))
throw new OpenGlException("eglMakeCurrent failed");
}
public IGlSurface CreateWindowSurface(IntPtr window)
{
var s = _egl.CreateWindowSurface(_display, _config, window, new[] {EGL_NONE, EGL_NONE});
if (s == IntPtr.Zero)
throw new OpenGlException("eglCreateWindowSurface failed");
return new EglSurface(this, s);
}
public int SampleCount
{
get
{
_egl.GetConfigAttrib(_display, _config, EGL_SAMPLES, out var rv);
return rv;
}
}
public int StencilSize
{
get
{
_egl.GetConfigAttrib(_display, _config, EGL_STENCIL_SIZE, out var rv);
return rv;
}
}
class EglSurface : SafeHandle, IGlSurface
{
private readonly EglDisplay _display;
public EglSurface(EglDisplay display, IntPtr surface) : base(surface, true)
{
_display = display;
}
protected override bool ReleaseHandle()
{
_display._egl.DestroySurface(_display._display, handle);
return true;
}
public override bool IsInvalid => handle == IntPtr.Zero;
public IGlDisplay Display => _display;
public void SwapBuffers() => _display._egl.SwapBuffers(_display._display, handle);
}
class EglContext : IGlContext
{
private readonly EglDisplay _disp;
public EglContext(EglDisplay display, IntPtr ctx, IntPtr offscreenSurface)
{
_disp = display;
Context = ctx;
OffscreenSurface = offscreenSurface;
}
public IntPtr Context { get; }
public IntPtr OffscreenSurface { get; }
public IGlDisplay Display => _disp;
public void MakeCurrent(IGlSurface surface)
{
var surf = ((EglSurface)surface)?.DangerousGetHandle() ?? OffscreenSurface;
if (!_disp._egl.MakeCurrent(_disp._display, surf, surf, Context))
throw new OpenGlException("eglMakeCurrent failed");
}
}
}
}

39
src/Avalonia.OpenGL/EglGlPlatformFeature.cs

@ -0,0 +1,39 @@
using System;
using Avalonia.Logging;
namespace Avalonia.OpenGL
{
public class EglGlPlatformFeature : IWindowingPlatformGlFeature
{
public IGlDisplay Display { get; set; }
public IGlContext ImmediateContext { get; set; }
public IGlContext DeferredContext { get; set; }
public static void TryInitialize()
{
var feature = TryCreate();
if (feature != null)
AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToConstant(feature);
}
public static EglGlPlatformFeature TryCreate()
{
try
{
var disp = new EglDisplay();
var ctx = disp.CreateContext(null);
return new EglGlPlatformFeature
{
Display = disp,
ImmediateContext = ctx,
DeferredContext = disp.CreateContext(ctx)
};
}
catch(Exception e)
{
Logger.Error("OpenGL", null, "Unable to initialize EGL-based rendering: {0}", e);
return null;
}
}
}
}

80
src/Avalonia.OpenGL/EglGlPlatformSurface.cs

@ -0,0 +1,80 @@
using System;
namespace Avalonia.OpenGL
{
public class EglGlPlatformSurface : IGlPlatformSurface
{
public interface IEglWindowGlPlatformSurfaceInfo
{
IntPtr Handle { get; }
// TODO: Change to PixelSize struct once https://github.com/AvaloniaUI/Avalonia/pull/1889 is merged
System.Drawing.Size PixelSize { get; }
double Scaling { get; }
}
private readonly EglDisplay _display;
private readonly IGlContext _context;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
public EglGlPlatformSurface(EglDisplay display, IGlContext context, IEglWindowGlPlatformSurfaceInfo info)
{
_display = display;
_context = context;
_info = info;
}
public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
{
var glSurface = _display.CreateWindowSurface(_info.Handle);
return new RenderTarget(_context, glSurface, _info);
}
class RenderTarget : IGlPlatformSurfaceRenderTarget
{
private readonly IGlContext _context;
private readonly IGlSurface _glSurface;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
public RenderTarget(IGlContext context, IGlSurface glSurface, IEglWindowGlPlatformSurfaceInfo info)
{
_context = context;
_glSurface = glSurface;
_info = info;
}
public void Dispose() => _glSurface.Dispose();
public IGlPlatformSurfaceRenderingSession BeginDraw()
{
_context.MakeCurrent(_glSurface);
return new Session(_context, _glSurface, _info);
}
class Session : IGlPlatformSurfaceRenderingSession
{
private readonly IGlContext _context;
private readonly IGlSurface _glSurface;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
public Session(IGlContext context, IGlSurface glSurface, IEglWindowGlPlatformSurfaceInfo info)
{
_context = context;
_glSurface = glSurface;
_info = info;
}
public void Dispose()
{
_context.Display.GlInterface.Flush();
_glSurface.SwapBuffers();
_context.Display.ClearContext();
}
public IGlDisplay Display => _context.Display;
public System.Drawing.Size PixelSize => _info.PixelSize;
public double Scaling => _info.Scaling;
}
}
}
}

93
src/Avalonia.OpenGL/EglInterface.cs

@ -0,0 +1,93 @@
using System;
using Avalonia.Platform;
using Avalonia.Platform.Interop;
using static Avalonia.OpenGL.EglConsts;
namespace Avalonia.OpenGL
{
public class EglInterface : GlInterfaceBase
{
public EglInterface() : base(Load())
{
}
public EglInterface(string library) : base(Load(library))
{
}
static Func<string, bool, IntPtr> Load()
{
var os = AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetRuntimeInfo().OperatingSystem;
if(os == OperatingSystemType.Linux || os == OperatingSystemType.Android)
return Load("libEGL.so.1");
if (os == OperatingSystemType.WinNT)
return Load(@"libegl.dll");
throw new PlatformNotSupportedException();
}
static Func<string, bool, IntPtr> Load(string library)
{
var dyn = AvaloniaLocator.Current.GetService<IDynamicLibraryLoader>();
var lib = dyn.LoadLibrary(library);
return (s, o) => dyn.GetProcAddress(lib, s, o);
}
// ReSharper disable UnassignedGetOnlyAutoProperty
public delegate IntPtr EglGetDisplay(IntPtr nativeDisplay);
[EntryPoint("eglGetDisplay")]
public EglGetDisplay GetDisplay { get; }
public delegate IntPtr EglGetPlatformDisplayEXT(int platform, IntPtr nativeDisplay, int[] attrs);
[EntryPoint("eglGetPlatformDisplayEXT", true)]
public EglGetPlatformDisplayEXT GetPlatformDisplayEXT { get; }
public delegate bool EglInitialize(IntPtr display, out int major, out int minor);
[EntryPoint("eglInitialize")]
public EglInitialize Initialize { get; }
public delegate IntPtr EglGetProcAddress(Utf8Buffer proc);
[EntryPoint("eglGetProcAddress")]
public EglGetProcAddress GetProcAddress { get; }
public delegate bool EglBindApi(int api);
[EntryPoint("eglBindAPI")]
public EglBindApi BindApi { get; }
public delegate bool EglChooseConfig(IntPtr display, int[] attribs,
out IntPtr surfaceConfig, int numConfigs, out int choosenConfig);
[EntryPoint("eglChooseConfig")]
public EglChooseConfig ChooseConfig { get; }
public delegate IntPtr EglCreateContext(IntPtr display, IntPtr config,
IntPtr share, int[] attrs);
[EntryPoint("eglCreateContext")]
public EglCreateContext CreateContext { get; }
public delegate IntPtr EglCreatePBufferSurface(IntPtr display, IntPtr config, int[] attrs);
[EntryPoint("eglCreatePbufferSurface")]
public EglCreatePBufferSurface CreatePBufferSurface { get; }
public delegate bool EglMakeCurrent(IntPtr display, IntPtr draw, IntPtr read, IntPtr context);
[EntryPoint("eglMakeCurrent")]
public EglMakeCurrent MakeCurrent { get; }
public delegate void EglDisplaySurfaceVoidDelegate(IntPtr display, IntPtr surface);
[EntryPoint("eglDestroySurface")]
public EglDisplaySurfaceVoidDelegate DestroySurface { get; }
[EntryPoint("eglSwapBuffers")]
public EglDisplaySurfaceVoidDelegate SwapBuffers { get; }
public delegate IntPtr
EglCreateWindowSurface(IntPtr display, IntPtr config, IntPtr window, int[] attrs);
[EntryPoint("eglCreateWindowSurface")]
public EglCreateWindowSurface CreateWindowSurface { get; }
public delegate bool EglGetConfigAttrib(IntPtr display, IntPtr config, int attr, out int rv);
[EntryPoint("eglGetConfigAttrib")]
public EglGetConfigAttrib GetConfigAttrib { get; }
// ReSharper restore UnassignedGetOnlyAutoProperty
}
}

16
src/Avalonia.OpenGL/EntryPointAttribute.cs

@ -0,0 +1,16 @@
using System;
namespace Avalonia.OpenGL
{
class EntryPointAttribute : Attribute
{
public string EntryPoint { get; }
public bool Optional { get; }
public EntryPointAttribute(string entryPoint, bool optional = false)
{
EntryPoint = entryPoint;
Optional = optional;
}
}
}

789
src/Avalonia.OpenGL/GlConsts.cs

@ -0,0 +1,789 @@
// ReSharper disable UnusedMember.Global
// ReSharper disable IdentifierTypo
namespace Avalonia.OpenGL
{
public static class GlConsts
{
public const int GL_BYTE = 0x1400;
public const int GL_UNSIGNED_BYTE = 0x1401;
public const int GL_SHORT = 0x1402;
public const int GL_UNSIGNED_SHORT = 0x1403;
public const int GL_INT = 0x1404;
public const int GL_UNSIGNED_INT = 0x1405;
public const int GL_FLOAT = 0x1406;
public const int GL_2_BYTES = 0x1407;
public const int GL_3_BYTES = 0x1408;
public const int GL_4_BYTES = 0x1409;
public const int GL_DOUBLE = 0x140A;
public const int GL_POINTS = 0x0000;
public const int GL_LINES = 0x0001;
public const int GL_LINE_LOOP = 0x0002;
public const int GL_LINE_STRIP = 0x0003;
public const int GL_TRIANGLES = 0x0004;
public const int GL_TRIANGLE_STRIP = 0x0005;
public const int GL_TRIANGLE_FAN = 0x0006;
public const int GL_QUADS = 0x0007;
public const int GL_QUAD_STRIP = 0x0008;
public const int GL_POLYGON = 0x0009;
public const int GL_VERTEX_ARRAY = 0x8074;
public const int GL_NORMAL_ARRAY = 0x8075;
public const int GL_COLOR_ARRAY = 0x8076;
public const int GL_INDEX_ARRAY = 0x8077;
public const int GL_TEXTURE_COORD_ARRAY = 0x8078;
public const int GL_EDGE_FLAG_ARRAY = 0x8079;
public const int GL_VERTEX_ARRAY_SIZE = 0x807A;
public const int GL_VERTEX_ARRAY_TYPE = 0x807B;
public const int GL_VERTEX_ARRAY_STRIDE = 0x807C;
public const int GL_NORMAL_ARRAY_TYPE = 0x807E;
public const int GL_NORMAL_ARRAY_STRIDE = 0x807F;
public const int GL_COLOR_ARRAY_SIZE = 0x8081;
public const int GL_COLOR_ARRAY_TYPE = 0x8082;
public const int GL_COLOR_ARRAY_STRIDE = 0x8083;
public const int GL_INDEX_ARRAY_TYPE = 0x8085;
public const int GL_INDEX_ARRAY_STRIDE = 0x8086;
public const int GL_TEXTURE_COORD_ARRAY_SIZE = 0x8088;
public const int GL_TEXTURE_COORD_ARRAY_TYPE = 0x8089;
public const int GL_TEXTURE_COORD_ARRAY_STRIDE = 0x808A;
public const int GL_EDGE_FLAG_ARRAY_STRIDE = 0x808C;
public const int GL_VERTEX_ARRAY_POINTER = 0x808E;
public const int GL_NORMAL_ARRAY_POINTER = 0x808F;
public const int GL_COLOR_ARRAY_POINTER = 0x8090;
public const int GL_INDEX_ARRAY_POINTER = 0x8091;
public const int GL_TEXTURE_COORD_ARRAY_POINTER = 0x8092;
public const int GL_EDGE_FLAG_ARRAY_POINTER = 0x8093;
public const int GL_V2F = 0x2A20;
public const int GL_V3F = 0x2A21;
public const int GL_C4UB_V2F = 0x2A22;
public const int GL_C4UB_V3F = 0x2A23;
public const int GL_C3F_V3F = 0x2A24;
public const int GL_N3F_V3F = 0x2A25;
public const int GL_C4F_N3F_V3F = 0x2A26;
public const int GL_T2F_V3F = 0x2A27;
public const int GL_T4F_V4F = 0x2A28;
public const int GL_T2F_C4UB_V3F = 0x2A29;
public const int GL_T2F_C3F_V3F = 0x2A2A;
public const int GL_T2F_N3F_V3F = 0x2A2B;
public const int GL_T2F_C4F_N3F_V3F = 0x2A2C;
public const int GL_T4F_C4F_N3F_V4F = 0x2A2D;
public const int GL_MATRIX_MODE = 0x0BA0;
public const int GL_MODELVIEW = 0x1700;
public const int GL_PROJECTION = 0x1701;
public const int GL_TEXTURE = 0x1702;
public const int GL_POINT_SMOOTH = 0x0B10;
public const int GL_POINT_SIZE = 0x0B11;
public const int GL_POINT_SIZE_GRANULARITY = 0x0B13;
public const int GL_POINT_SIZE_RANGE = 0x0B12;
public const int GL_LINE_SMOOTH = 0x0B20;
public const int GL_LINE_STIPPLE = 0x0B24;
public const int GL_LINE_STIPPLE_PATTERN = 0x0B25;
public const int GL_LINE_STIPPLE_REPEAT = 0x0B26;
public const int GL_LINE_WIDTH = 0x0B21;
public const int GL_LINE_WIDTH_GRANULARITY = 0x0B23;
public const int GL_LINE_WIDTH_RANGE = 0x0B22;
public const int GL_POINT = 0x1B00;
public const int GL_LINE = 0x1B01;
public const int GL_FILL = 0x1B02;
public const int GL_CW = 0x0900;
public const int GL_CCW = 0x0901;
public const int GL_FRONT = 0x0404;
public const int GL_BACK = 0x0405;
public const int GL_POLYGON_MODE = 0x0B40;
public const int GL_POLYGON_SMOOTH = 0x0B41;
public const int GL_POLYGON_STIPPLE = 0x0B42;
public const int GL_EDGE_FLAG = 0x0B43;
public const int GL_CULL_FACE = 0x0B44;
public const int GL_CULL_FACE_MODE = 0x0B45;
public const int GL_FRONT_FACE = 0x0B46;
public const int GL_POLYGON_OFFSET_FACTOR = 0x8038;
public const int GL_POLYGON_OFFSET_UNITS = 0x2A00;
public const int GL_POLYGON_OFFSET_POINT = 0x2A01;
public const int GL_POLYGON_OFFSET_LINE = 0x2A02;
public const int GL_POLYGON_OFFSET_FILL = 0x8037;
public const int GL_COMPILE = 0x1300;
public const int GL_COMPILE_AND_EXECUTE = 0x1301;
public const int GL_LIST_BASE = 0x0B32;
public const int GL_LIST_INDEX = 0x0B33;
public const int GL_LIST_MODE = 0x0B30;
public const int GL_NEVER = 0x0200;
public const int GL_LESS = 0x0201;
public const int GL_EQUAL = 0x0202;
public const int GL_LEQUAL = 0x0203;
public const int GL_GREATER = 0x0204;
public const int GL_NOTEQUAL = 0x0205;
public const int GL_GEQUAL = 0x0206;
public const int GL_ALWAYS = 0x0207;
public const int GL_DEPTH_TEST = 0x0B71;
public const int GL_DEPTH_BITS = 0x0D56;
public const int GL_DEPTH_CLEAR_VALUE = 0x0B73;
public const int GL_DEPTH_FUNC = 0x0B74;
public const int GL_DEPTH_RANGE = 0x0B70;
public const int GL_DEPTH_WRITEMASK = 0x0B72;
public const int GL_DEPTH_COMPONENT = 0x1902;
public const int GL_LIGHTING = 0x0B50;
public const int GL_LIGHT0 = 0x4000;
public const int GL_LIGHT1 = 0x4001;
public const int GL_LIGHT2 = 0x4002;
public const int GL_LIGHT3 = 0x4003;
public const int GL_LIGHT4 = 0x4004;
public const int GL_LIGHT5 = 0x4005;
public const int GL_LIGHT6 = 0x4006;
public const int GL_LIGHT7 = 0x4007;
public const int GL_SPOT_EXPONENT = 0x1205;
public const int GL_SPOT_CUTOFF = 0x1206;
public const int GL_CONSTANT_ATTENUATION = 0x1207;
public const int GL_LINEAR_ATTENUATION = 0x1208;
public const int GL_QUADRATIC_ATTENUATION = 0x1209;
public const int GL_AMBIENT = 0x1200;
public const int GL_DIFFUSE = 0x1201;
public const int GL_SPECULAR = 0x1202;
public const int GL_SHININESS = 0x1601;
public const int GL_EMISSION = 0x1600;
public const int GL_POSITION = 0x1203;
public const int GL_SPOT_DIRECTION = 0x1204;
public const int GL_AMBIENT_AND_DIFFUSE = 0x1602;
public const int GL_COLOR_INDEXES = 0x1603;
public const int GL_LIGHT_MODEL_TWO_SIDE = 0x0B52;
public const int GL_LIGHT_MODEL_LOCAL_VIEWER = 0x0B51;
public const int GL_LIGHT_MODEL_AMBIENT = 0x0B53;
public const int GL_FRONT_AND_BACK = 0x0408;
public const int GL_SHADE_MODEL = 0x0B54;
public const int GL_FLAT = 0x1D00;
public const int GL_SMOOTH = 0x1D01;
public const int GL_COLOR_MATERIAL = 0x0B57;
public const int GL_COLOR_MATERIAL_FACE = 0x0B55;
public const int GL_COLOR_MATERIAL_PARAMETER = 0x0B56;
public const int GL_NORMALIZE = 0x0BA1;
public const int GL_CLIP_PLANE0 = 0x3000;
public const int GL_CLIP_PLANE1 = 0x3001;
public const int GL_CLIP_PLANE2 = 0x3002;
public const int GL_CLIP_PLANE3 = 0x3003;
public const int GL_CLIP_PLANE4 = 0x3004;
public const int GL_CLIP_PLANE5 = 0x3005;
public const int GL_ACCUM_RED_BITS = 0x0D58;
public const int GL_ACCUM_GREEN_BITS = 0x0D59;
public const int GL_ACCUM_BLUE_BITS = 0x0D5A;
public const int GL_ACCUM_ALPHA_BITS = 0x0D5B;
public const int GL_ACCUM_CLEAR_VALUE = 0x0B80;
public const int GL_ACCUM = 0x0100;
public const int GL_ADD = 0x0104;
public const int GL_LOAD = 0x0101;
public const int GL_MULT = 0x0103;
public const int GL_RETURN = 0x0102;
public const int GL_ALPHA_TEST = 0x0BC0;
public const int GL_ALPHA_TEST_REF = 0x0BC2;
public const int GL_ALPHA_TEST_FUNC = 0x0BC1;
public const int GL_BLEND = 0x0BE2;
public const int GL_BLEND_SRC = 0x0BE1;
public const int GL_BLEND_DST = 0x0BE0;
public const int GL_SRC_COLOR = 0x0300;
public const int GL_ONE_MINUS_SRC_COLOR = 0x0301;
public const int GL_SRC_ALPHA = 0x0302;
public const int GL_ONE_MINUS_SRC_ALPHA = 0x0303;
public const int GL_DST_ALPHA = 0x0304;
public const int GL_ONE_MINUS_DST_ALPHA = 0x0305;
public const int GL_DST_COLOR = 0x0306;
public const int GL_ONE_MINUS_DST_COLOR = 0x0307;
public const int GL_SRC_ALPHA_SATURATE = 0x0308;
public const int GL_FEEDBACK = 0x1C01;
public const int GL_RENDER = 0x1C00;
public const int GL_SELECT = 0x1C02;
public const int GL_2D = 0x0600;
public const int GL_3D = 0x0601;
public const int GL_3D_COLOR = 0x0602;
public const int GL_3D_COLOR_TEXTURE = 0x0603;
public const int GL_4D_COLOR_TEXTURE = 0x0604;
public const int GL_POINT_TOKEN = 0x0701;
public const int GL_LINE_TOKEN = 0x0702;
public const int GL_LINE_RESET_TOKEN = 0x0707;
public const int GL_POLYGON_TOKEN = 0x0703;
public const int GL_BITMAP_TOKEN = 0x0704;
public const int GL_DRAW_PIXEL_TOKEN = 0x0705;
public const int GL_COPY_PIXEL_TOKEN = 0x0706;
public const int GL_PASS_THROUGH_TOKEN = 0x0700;
public const int GL_FEEDBACK_BUFFER_POINTER = 0x0DF0;
public const int GL_FEEDBACK_BUFFER_SIZE = 0x0DF1;
public const int GL_FEEDBACK_BUFFER_TYPE = 0x0DF2;
public const int GL_SELECTION_BUFFER_POINTER = 0x0DF3;
public const int GL_SELECTION_BUFFER_SIZE = 0x0DF4;
public const int GL_FOG = 0x0B60;
public const int GL_FOG_MODE = 0x0B65;
public const int GL_FOG_DENSITY = 0x0B62;
public const int GL_FOG_COLOR = 0x0B66;
public const int GL_FOG_INDEX = 0x0B61;
public const int GL_FOG_START = 0x0B63;
public const int GL_FOG_END = 0x0B64;
public const int GL_LINEAR = 0x2601;
public const int GL_EXP = 0x0800;
public const int GL_EXP2 = 0x0801;
public const int GL_LOGIC_OP = 0x0BF1;
public const int GL_INDEX_LOGIC_OP = 0x0BF1;
public const int GL_COLOR_LOGIC_OP = 0x0BF2;
public const int GL_LOGIC_OP_MODE = 0x0BF0;
public const int GL_CLEAR = 0x1500;
public const int GL_SET = 0x150F;
public const int GL_COPY = 0x1503;
public const int GL_COPY_INVERTED = 0x150C;
public const int GL_NOOP = 0x1505;
public const int GL_INVERT = 0x150A;
public const int GL_AND = 0x1501;
public const int GL_NAND = 0x150E;
public const int GL_OR = 0x1507;
public const int GL_NOR = 0x1508;
public const int GL_XOR = 0x1506;
public const int GL_EQUIV = 0x1509;
public const int GL_AND_REVERSE = 0x1502;
public const int GL_AND_INVERTED = 0x1504;
public const int GL_OR_REVERSE = 0x150B;
public const int GL_OR_INVERTED = 0x150D;
public const int GL_STENCIL_BITS = 0x0D57;
public const int GL_STENCIL_TEST = 0x0B90;
public const int GL_STENCIL_CLEAR_VALUE = 0x0B91;
public const int GL_STENCIL_FUNC = 0x0B92;
public const int GL_STENCIL_VALUE_MASK = 0x0B93;
public const int GL_STENCIL_FAIL = 0x0B94;
public const int GL_STENCIL_PASS_DEPTH_FAIL = 0x0B95;
public const int GL_STENCIL_PASS_DEPTH_PASS = 0x0B96;
public const int GL_STENCIL_REF = 0x0B97;
public const int GL_STENCIL_WRITEMASK = 0x0B98;
public const int GL_STENCIL_INDEX = 0x1901;
public const int GL_KEEP = 0x1E00;
public const int GL_REPLACE = 0x1E01;
public const int GL_INCR = 0x1E02;
public const int GL_DECR = 0x1E03;
public const int GL_LEFT = 0x0406;
public const int GL_RIGHT = 0x0407;
public const int GL_FRONT_LEFT = 0x0400;
public const int GL_FRONT_RIGHT = 0x0401;
public const int GL_BACK_LEFT = 0x0402;
public const int GL_BACK_RIGHT = 0x0403;
public const int GL_AUX0 = 0x0409;
public const int GL_AUX1 = 0x040A;
public const int GL_AUX2 = 0x040B;
public const int GL_AUX3 = 0x040C;
public const int GL_COLOR_INDEX = 0x1900;
public const int GL_RED = 0x1903;
public const int GL_GREEN = 0x1904;
public const int GL_BLUE = 0x1905;
public const int GL_ALPHA = 0x1906;
public const int GL_LUMINANCE = 0x1909;
public const int GL_LUMINANCE_ALPHA = 0x190A;
public const int GL_ALPHA_BITS = 0x0D55;
public const int GL_RED_BITS = 0x0D52;
public const int GL_GREEN_BITS = 0x0D53;
public const int GL_BLUE_BITS = 0x0D54;
public const int GL_INDEX_BITS = 0x0D51;
public const int GL_SUBPIXEL_BITS = 0x0D50;
public const int GL_AUX_BUFFERS = 0x0C00;
public const int GL_READ_BUFFER = 0x0C02;
public const int GL_DRAW_BUFFER = 0x0C01;
public const int GL_DOUBLEBUFFER = 0x0C32;
public const int GL_STEREO = 0x0C33;
public const int GL_BITMAP = 0x1A00;
public const int GL_COLOR = 0x1800;
public const int GL_DEPTH = 0x1801;
public const int GL_STENCIL = 0x1802;
public const int GL_DITHER = 0x0BD0;
public const int GL_RGB = 0x1907;
public const int GL_RGBA = 0x1908;
public const int GL_MAX_LIST_NESTING = 0x0B31;
public const int GL_MAX_EVAL_ORDER = 0x0D30;
public const int GL_MAX_LIGHTS = 0x0D31;
public const int GL_MAX_CLIP_PLANES = 0x0D32;
public const int GL_MAX_TEXTURE_SIZE = 0x0D33;
public const int GL_MAX_PIXEL_MAP_TABLE = 0x0D34;
public const int GL_MAX_ATTRIB_STACK_DEPTH = 0x0D35;
public const int GL_MAX_MODELVIEW_STACK_DEPTH = 0x0D36;
public const int GL_MAX_NAME_STACK_DEPTH = 0x0D37;
public const int GL_MAX_PROJECTION_STACK_DEPTH = 0x0D38;
public const int GL_MAX_TEXTURE_STACK_DEPTH = 0x0D39;
public const int GL_MAX_VIEWPORT_DIMS = 0x0D3A;
public const int GL_MAX_CLIENT_ATTRIB_STACK_DEPTH = 0x0D3B;
public const int GL_ATTRIB_STACK_DEPTH = 0x0BB0;
public const int GL_CLIENT_ATTRIB_STACK_DEPTH = 0x0BB1;
public const int GL_COLOR_CLEAR_VALUE = 0x0C22;
public const int GL_COLOR_WRITEMASK = 0x0C23;
public const int GL_CURRENT_INDEX = 0x0B01;
public const int GL_CURRENT_COLOR = 0x0B00;
public const int GL_CURRENT_NORMAL = 0x0B02;
public const int GL_CURRENT_RASTER_COLOR = 0x0B04;
public const int GL_CURRENT_RASTER_DISTANCE = 0x0B09;
public const int GL_CURRENT_RASTER_INDEX = 0x0B05;
public const int GL_CURRENT_RASTER_POSITION = 0x0B07;
public const int GL_CURRENT_RASTER_TEXTURE_COORDS = 0x0B06;
public const int GL_CURRENT_RASTER_POSITION_VALID = 0x0B08;
public const int GL_CURRENT_TEXTURE_COORDS = 0x0B03;
public const int GL_INDEX_CLEAR_VALUE = 0x0C20;
public const int GL_INDEX_MODE = 0x0C30;
public const int GL_INDEX_WRITEMASK = 0x0C21;
public const int GL_MODELVIEW_MATRIX = 0x0BA6;
public const int GL_MODELVIEW_STACK_DEPTH = 0x0BA3;
public const int GL_NAME_STACK_DEPTH = 0x0D70;
public const int GL_PROJECTION_MATRIX = 0x0BA7;
public const int GL_PROJECTION_STACK_DEPTH = 0x0BA4;
public const int GL_RENDER_MODE = 0x0C40;
public const int GL_RGBA_MODE = 0x0C31;
public const int GL_TEXTURE_MATRIX = 0x0BA8;
public const int GL_TEXTURE_STACK_DEPTH = 0x0BA5;
public const int GL_VIEWPORT = 0x0BA2;
public const int GL_AUTO_NORMAL = 0x0D80;
public const int GL_MAP1_COLOR_4 = 0x0D90;
public const int GL_MAP1_INDEX = 0x0D91;
public const int GL_MAP1_NORMAL = 0x0D92;
public const int GL_MAP1_TEXTURE_COORD_1 = 0x0D93;
public const int GL_MAP1_TEXTURE_COORD_2 = 0x0D94;
public const int GL_MAP1_TEXTURE_COORD_3 = 0x0D95;
public const int GL_MAP1_TEXTURE_COORD_4 = 0x0D96;
public const int GL_MAP1_VERTEX_3 = 0x0D97;
public const int GL_MAP1_VERTEX_4 = 0x0D98;
public const int GL_MAP2_COLOR_4 = 0x0DB0;
public const int GL_MAP2_INDEX = 0x0DB1;
public const int GL_MAP2_NORMAL = 0x0DB2;
public const int GL_MAP2_TEXTURE_COORD_1 = 0x0DB3;
public const int GL_MAP2_TEXTURE_COORD_2 = 0x0DB4;
public const int GL_MAP2_TEXTURE_COORD_3 = 0x0DB5;
public const int GL_MAP2_TEXTURE_COORD_4 = 0x0DB6;
public const int GL_MAP2_VERTEX_3 = 0x0DB7;
public const int GL_MAP2_VERTEX_4 = 0x0DB8;
public const int GL_MAP1_GRID_DOMAIN = 0x0DD0;
public const int GL_MAP1_GRID_SEGMENTS = 0x0DD1;
public const int GL_MAP2_GRID_DOMAIN = 0x0DD2;
public const int GL_MAP2_GRID_SEGMENTS = 0x0DD3;
public const int GL_COEFF = 0x0A00;
public const int GL_ORDER = 0x0A01;
public const int GL_DOMAIN = 0x0A02;
public const int GL_PERSPECTIVE_CORRECTION_HINT = 0x0C50;
public const int GL_POINT_SMOOTH_HINT = 0x0C51;
public const int GL_LINE_SMOOTH_HINT = 0x0C52;
public const int GL_POLYGON_SMOOTH_HINT = 0x0C53;
public const int GL_FOG_HINT = 0x0C54;
public const int GL_DONT_CARE = 0x1100;
public const int GL_FASTEST = 0x1101;
public const int GL_NICEST = 0x1102;
public const int GL_SCISSOR_BOX = 0x0C10;
public const int GL_SCISSOR_TEST = 0x0C11;
public const int GL_MAP_COLOR = 0x0D10;
public const int GL_MAP_STENCIL = 0x0D11;
public const int GL_INDEX_SHIFT = 0x0D12;
public const int GL_INDEX_OFFSET = 0x0D13;
public const int GL_RED_SCALE = 0x0D14;
public const int GL_RED_BIAS = 0x0D15;
public const int GL_GREEN_SCALE = 0x0D18;
public const int GL_GREEN_BIAS = 0x0D19;
public const int GL_BLUE_SCALE = 0x0D1A;
public const int GL_BLUE_BIAS = 0x0D1B;
public const int GL_ALPHA_SCALE = 0x0D1C;
public const int GL_ALPHA_BIAS = 0x0D1D;
public const int GL_DEPTH_SCALE = 0x0D1E;
public const int GL_DEPTH_BIAS = 0x0D1F;
public const int GL_PIXEL_MAP_S_TO_S_SIZE = 0x0CB1;
public const int GL_PIXEL_MAP_I_TO_I_SIZE = 0x0CB0;
public const int GL_PIXEL_MAP_I_TO_R_SIZE = 0x0CB2;
public const int GL_PIXEL_MAP_I_TO_G_SIZE = 0x0CB3;
public const int GL_PIXEL_MAP_I_TO_B_SIZE = 0x0CB4;
public const int GL_PIXEL_MAP_I_TO_A_SIZE = 0x0CB5;
public const int GL_PIXEL_MAP_R_TO_R_SIZE = 0x0CB6;
public const int GL_PIXEL_MAP_G_TO_G_SIZE = 0x0CB7;
public const int GL_PIXEL_MAP_B_TO_B_SIZE = 0x0CB8;
public const int GL_PIXEL_MAP_A_TO_A_SIZE = 0x0CB9;
public const int GL_PIXEL_MAP_S_TO_S = 0x0C71;
public const int GL_PIXEL_MAP_I_TO_I = 0x0C70;
public const int GL_PIXEL_MAP_I_TO_R = 0x0C72;
public const int GL_PIXEL_MAP_I_TO_G = 0x0C73;
public const int GL_PIXEL_MAP_I_TO_B = 0x0C74;
public const int GL_PIXEL_MAP_I_TO_A = 0x0C75;
public const int GL_PIXEL_MAP_R_TO_R = 0x0C76;
public const int GL_PIXEL_MAP_G_TO_G = 0x0C77;
public const int GL_PIXEL_MAP_B_TO_B = 0x0C78;
public const int GL_PIXEL_MAP_A_TO_A = 0x0C79;
public const int GL_PACK_ALIGNMENT = 0x0D05;
public const int GL_PACK_LSB_FIRST = 0x0D01;
public const int GL_PACK_ROW_LENGTH = 0x0D02;
public const int GL_PACK_SKIP_PIXELS = 0x0D04;
public const int GL_PACK_SKIP_ROWS = 0x0D03;
public const int GL_PACK_SWAP_BYTES = 0x0D00;
public const int GL_UNPACK_ALIGNMENT = 0x0CF5;
public const int GL_UNPACK_LSB_FIRST = 0x0CF1;
public const int GL_UNPACK_ROW_LENGTH = 0x0CF2;
public const int GL_UNPACK_SKIP_PIXELS = 0x0CF4;
public const int GL_UNPACK_SKIP_ROWS = 0x0CF3;
public const int GL_UNPACK_SWAP_BYTES = 0x0CF0;
public const int GL_ZOOM_X = 0x0D16;
public const int GL_ZOOM_Y = 0x0D17;
public const int GL_TEXTURE_ENV = 0x2300;
public const int GL_TEXTURE_ENV_MODE = 0x2200;
public const int GL_TEXTURE_1D = 0x0DE0;
public const int GL_TEXTURE_2D = 0x0DE1;
public const int GL_TEXTURE_WRAP_S = 0x2802;
public const int GL_TEXTURE_WRAP_T = 0x2803;
public const int GL_TEXTURE_MAG_FILTER = 0x2800;
public const int GL_TEXTURE_MIN_FILTER = 0x2801;
public const int GL_TEXTURE_ENV_COLOR = 0x2201;
public const int GL_TEXTURE_GEN_S = 0x0C60;
public const int GL_TEXTURE_GEN_T = 0x0C61;
public const int GL_TEXTURE_GEN_R = 0x0C62;
public const int GL_TEXTURE_GEN_Q = 0x0C63;
public const int GL_TEXTURE_GEN_MODE = 0x2500;
public const int GL_TEXTURE_BORDER_COLOR = 0x1004;
public const int GL_TEXTURE_WIDTH = 0x1000;
public const int GL_TEXTURE_HEIGHT = 0x1001;
public const int GL_TEXTURE_BORDER = 0x1005;
public const int GL_TEXTURE_COMPONENTS = 0x1003;
public const int GL_TEXTURE_RED_SIZE = 0x805C;
public const int GL_TEXTURE_GREEN_SIZE = 0x805D;
public const int GL_TEXTURE_BLUE_SIZE = 0x805E;
public const int GL_TEXTURE_ALPHA_SIZE = 0x805F;
public const int GL_TEXTURE_LUMINANCE_SIZE = 0x8060;
public const int GL_TEXTURE_INTENSITY_SIZE = 0x8061;
public const int GL_NEAREST_MIPMAP_NEAREST = 0x2700;
public const int GL_NEAREST_MIPMAP_LINEAR = 0x2702;
public const int GL_LINEAR_MIPMAP_NEAREST = 0x2701;
public const int GL_LINEAR_MIPMAP_LINEAR = 0x2703;
public const int GL_OBJECT_LINEAR = 0x2401;
public const int GL_OBJECT_PLANE = 0x2501;
public const int GL_EYE_LINEAR = 0x2400;
public const int GL_EYE_PLANE = 0x2502;
public const int GL_SPHERE_MAP = 0x2402;
public const int GL_DECAL = 0x2101;
public const int GL_MODULATE = 0x2100;
public const int GL_NEAREST = 0x2600;
public const int GL_REPEAT = 0x2901;
public const int GL_CLAMP = 0x2900;
public const int GL_S = 0x2000;
public const int GL_T = 0x2001;
public const int GL_R = 0x2002;
public const int GL_Q = 0x2003;
public const int GL_VENDOR = 0x1F00;
public const int GL_RENDERER = 0x1F01;
public const int GL_VERSION = 0x1F02;
public const int GL_EXTENSIONS = 0x1F03;
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_CURRENT_BIT = 0x00000001;
public const int GL_POINT_BIT = 0x00000002;
public const int GL_LINE_BIT = 0x00000004;
public const int GL_POLYGON_BIT = 0x00000008;
public const int GL_POLYGON_STIPPLE_BIT = 0x00000010;
public const int GL_PIXEL_MODE_BIT = 0x00000020;
public const int GL_LIGHTING_BIT = 0x00000040;
public const int GL_FOG_BIT = 0x00000080;
public const int GL_DEPTH_BUFFER_BIT = 0x00000100;
public const int GL_ACCUM_BUFFER_BIT = 0x00000200;
public const int GL_STENCIL_BUFFER_BIT = 0x00000400;
public const int GL_VIEWPORT_BIT = 0x00000800;
public const int GL_TRANSFORM_BIT = 0x00001000;
public const int GL_ENABLE_BIT = 0x00002000;
public const int GL_COLOR_BUFFER_BIT = 0x00004000;
public const int GL_HINT_BIT = 0x00008000;
public const int GL_EVAL_BIT = 0x00010000;
public const int GL_LIST_BIT = 0x00020000;
public const int GL_TEXTURE_BIT = 0x00040000;
public const int GL_SCISSOR_BIT = 0x00080000;
public const int GL_ALL_ATTRIB_BITS = -1;
public const int GL_PROXY_TEXTURE_1D = 0x8063;
public const int GL_PROXY_TEXTURE_2D = 0x8064;
public const int GL_TEXTURE_PRIORITY = 0x8066;
public const int GL_TEXTURE_RESIDENT = 0x8067;
public const int GL_TEXTURE_BINDING_1D = 0x8068;
public const int GL_TEXTURE_BINDING_2D = 0x8069;
public const int GL_TEXTURE_INTERNAL_FORMAT = 0x1003;
public const int GL_ALPHA4 = 0x803B;
public const int GL_ALPHA8 = 0x803C;
public const int GL_ALPHA12 = 0x803D;
public const int GL_ALPHA16 = 0x803E;
public const int GL_LUMINANCE4 = 0x803F;
public const int GL_LUMINANCE8 = 0x8040;
public const int GL_LUMINANCE12 = 0x8041;
public const int GL_LUMINANCE16 = 0x8042;
public const int GL_LUMINANCE4_ALPHA4 = 0x8043;
public const int GL_LUMINANCE6_ALPHA2 = 0x8044;
public const int GL_LUMINANCE8_ALPHA8 = 0x8045;
public const int GL_LUMINANCE12_ALPHA4 = 0x8046;
public const int GL_LUMINANCE12_ALPHA12 = 0x8047;
public const int GL_LUMINANCE16_ALPHA16 = 0x8048;
public const int GL_INTENSITY = 0x8049;
public const int GL_INTENSITY4 = 0x804A;
public const int GL_INTENSITY8 = 0x804B;
public const int GL_INTENSITY12 = 0x804C;
public const int GL_INTENSITY16 = 0x804D;
public const int GL_R3_G3_B2 = 0x2A10;
public const int GL_RGB4 = 0x804F;
public const int GL_RGB5 = 0x8050;
public const int GL_RGB8 = 0x8051;
public const int GL_RGB10 = 0x8052;
public const int GL_RGB12 = 0x8053;
public const int GL_RGB16 = 0x8054;
public const int GL_RGBA2 = 0x8055;
public const int GL_RGBA4 = 0x8056;
public const int GL_RGB5_A1 = 0x8057;
public const int GL_RGBA8 = 0x8058;
public const int GL_RGB10_A2 = 0x8059;
public const int GL_RGBA12 = 0x805A;
public const int GL_RGBA16 = 0x805B;
public const int GL_CLIENT_PIXEL_STORE_BIT = 0x00000001;
public const int GL_CLIENT_VERTEX_ARRAY_BIT = 0x00000002;
public const int GL_ALL_CLIENT_ATTRIB_BITS = -1;
public const int GL_CLIENT_ALL_ATTRIB_BITS = -1;
public const int GL_RESCALE_NORMAL = 0x803A;
public const int GL_CLAMP_TO_EDGE = 0x812F;
public const int GL_MAX_ELEMENTS_VERTICES = 0x80E8;
public const int GL_MAX_ELEMENTS_INDICES = 0x80E9;
public const int GL_BGR = 0x80E0;
public const int GL_BGRA = 0x80E1;
public const int GL_UNSIGNED_BYTE_3_3_2 = 0x8032;
public const int GL_UNSIGNED_BYTE_2_3_3_REV = 0x8362;
public const int GL_UNSIGNED_SHORT_5_6_5 = 0x8363;
public const int GL_UNSIGNED_SHORT_5_6_5_REV = 0x8364;
public const int GL_UNSIGNED_SHORT_4_4_4_4 = 0x8033;
public const int GL_UNSIGNED_SHORT_4_4_4_4_REV = 0x8365;
public const int GL_UNSIGNED_SHORT_5_5_5_1 = 0x8034;
public const int GL_UNSIGNED_SHORT_1_5_5_5_REV = 0x8366;
public const int GL_UNSIGNED_INT_8_8_8_8 = 0x8035;
public const int GL_UNSIGNED_INT_8_8_8_8_REV = 0x8367;
public const int GL_UNSIGNED_INT_10_10_10_2 = 0x8036;
public const int GL_UNSIGNED_INT_2_10_10_10_REV = 0x8368;
public const int GL_LIGHT_MODEL_COLOR_CONTROL = 0x81F8;
public const int GL_SINGLE_COLOR = 0x81F9;
public const int GL_SEPARATE_SPECULAR_COLOR = 0x81FA;
public const int GL_TEXTURE_MIN_LOD = 0x813A;
public const int GL_TEXTURE_MAX_LOD = 0x813B;
public const int GL_TEXTURE_BASE_LEVEL = 0x813C;
public const int GL_TEXTURE_MAX_LEVEL = 0x813D;
public const int GL_SMOOTH_POINT_SIZE_RANGE = 0x0B12;
public const int GL_SMOOTH_POINT_SIZE_GRANULARITY = 0x0B13;
public const int GL_SMOOTH_LINE_WIDTH_RANGE = 0x0B22;
public const int GL_SMOOTH_LINE_WIDTH_GRANULARITY = 0x0B23;
public const int GL_ALIASED_POINT_SIZE_RANGE = 0x846D;
public const int GL_ALIASED_LINE_WIDTH_RANGE = 0x846E;
public const int GL_PACK_SKIP_IMAGES = 0x806B;
public const int GL_PACK_IMAGE_HEIGHT = 0x806C;
public const int GL_UNPACK_SKIP_IMAGES = 0x806D;
public const int GL_UNPACK_IMAGE_HEIGHT = 0x806E;
public const int GL_TEXTURE_3D = 0x806F;
public const int GL_PROXY_TEXTURE_3D = 0x8070;
public const int GL_TEXTURE_DEPTH = 0x8071;
public const int GL_TEXTURE_WRAP_R = 0x8072;
public const int GL_MAX_3D_TEXTURE_SIZE = 0x8073;
public const int GL_TEXTURE_BINDING_3D = 0x806A;
public const int GL_CONSTANT_COLOR = 0x8001;
public const int GL_ONE_MINUS_CONSTANT_COLOR = 0x8002;
public const int GL_CONSTANT_ALPHA = 0x8003;
public const int GL_ONE_MINUS_CONSTANT_ALPHA = 0x8004;
public const int GL_COLOR_TABLE = 0x80D0;
public const int GL_POST_CONVOLUTION_COLOR_TABLE = 0x80D1;
public const int GL_POST_COLOR_MATRIX_COLOR_TABLE = 0x80D2;
public const int GL_PROXY_COLOR_TABLE = 0x80D3;
public const int GL_PROXY_POST_CONVOLUTION_COLOR_TABLE = 0x80D4;
public const int GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE = 0x80D5;
public const int GL_COLOR_TABLE_SCALE = 0x80D6;
public const int GL_COLOR_TABLE_BIAS = 0x80D7;
public const int GL_COLOR_TABLE_FORMAT = 0x80D8;
public const int GL_COLOR_TABLE_WIDTH = 0x80D9;
public const int GL_COLOR_TABLE_RED_SIZE = 0x80DA;
public const int GL_COLOR_TABLE_GREEN_SIZE = 0x80DB;
public const int GL_COLOR_TABLE_BLUE_SIZE = 0x80DC;
public const int GL_COLOR_TABLE_ALPHA_SIZE = 0x80DD;
public const int GL_COLOR_TABLE_LUMINANCE_SIZE = 0x80DE;
public const int GL_COLOR_TABLE_INTENSITY_SIZE = 0x80DF;
public const int GL_CONVOLUTION_1D = 0x8010;
public const int GL_CONVOLUTION_2D = 0x8011;
public const int GL_SEPARABLE_2D = 0x8012;
public const int GL_CONVOLUTION_BORDER_MODE = 0x8013;
public const int GL_CONVOLUTION_FILTER_SCALE = 0x8014;
public const int GL_CONVOLUTION_FILTER_BIAS = 0x8015;
public const int GL_REDUCE = 0x8016;
public const int GL_CONVOLUTION_FORMAT = 0x8017;
public const int GL_CONVOLUTION_WIDTH = 0x8018;
public const int GL_CONVOLUTION_HEIGHT = 0x8019;
public const int GL_MAX_CONVOLUTION_WIDTH = 0x801A;
public const int GL_MAX_CONVOLUTION_HEIGHT = 0x801B;
public const int GL_POST_CONVOLUTION_RED_SCALE = 0x801C;
public const int GL_POST_CONVOLUTION_GREEN_SCALE = 0x801D;
public const int GL_POST_CONVOLUTION_BLUE_SCALE = 0x801E;
public const int GL_POST_CONVOLUTION_ALPHA_SCALE = 0x801F;
public const int GL_POST_CONVOLUTION_RED_BIAS = 0x8020;
public const int GL_POST_CONVOLUTION_GREEN_BIAS = 0x8021;
public const int GL_POST_CONVOLUTION_BLUE_BIAS = 0x8022;
public const int GL_POST_CONVOLUTION_ALPHA_BIAS = 0x8023;
public const int GL_CONSTANT_BORDER = 0x8151;
public const int GL_REPLICATE_BORDER = 0x8153;
public const int GL_CONVOLUTION_BORDER_COLOR = 0x8154;
public const int GL_COLOR_MATRIX = 0x80B1;
public const int GL_COLOR_MATRIX_STACK_DEPTH = 0x80B2;
public const int GL_MAX_COLOR_MATRIX_STACK_DEPTH = 0x80B3;
public const int GL_POST_COLOR_MATRIX_RED_SCALE = 0x80B4;
public const int GL_POST_COLOR_MATRIX_GREEN_SCALE = 0x80B5;
public const int GL_POST_COLOR_MATRIX_BLUE_SCALE = 0x80B6;
public const int GL_POST_COLOR_MATRIX_ALPHA_SCALE = 0x80B7;
public const int GL_POST_COLOR_MATRIX_RED_BIAS = 0x80B8;
public const int GL_POST_COLOR_MATRIX_GREEN_BIAS = 0x80B9;
public const int GL_POST_COLOR_MATRIX_BLUE_BIAS = 0x80BA;
public const int GL_POST_COLOR_MATRIX_ALPHA_BIAS = 0x80BB;
public const int GL_HISTOGRAM = 0x8024;
public const int GL_PROXY_HISTOGRAM = 0x8025;
public const int GL_HISTOGRAM_WIDTH = 0x8026;
public const int GL_HISTOGRAM_FORMAT = 0x8027;
public const int GL_HISTOGRAM_RED_SIZE = 0x8028;
public const int GL_HISTOGRAM_GREEN_SIZE = 0x8029;
public const int GL_HISTOGRAM_BLUE_SIZE = 0x802A;
public const int GL_HISTOGRAM_ALPHA_SIZE = 0x802B;
public const int GL_HISTOGRAM_LUMINANCE_SIZE = 0x802C;
public const int GL_HISTOGRAM_SINK = 0x802D;
public const int GL_MINMAX = 0x802E;
public const int GL_MINMAX_FORMAT = 0x802F;
public const int GL_MINMAX_SINK = 0x8030;
public const int GL_TABLE_TOO_LARGE = 0x8031;
public const int GL_BLEND_EQUATION = 0x8009;
public const int GL_MIN = 0x8007;
public const int GL_MAX = 0x8008;
public const int GL_FUNC_ADD = 0x8006;
public const int GL_FUNC_SUBTRACT = 0x800A;
public const int GL_FUNC_REVERSE_SUBTRACT = 0x800B;
public const int GL_BLEND_COLOR = 0x8005;
public const int GL_TEXTURE0 = 0x84C0;
public const int GL_TEXTURE1 = 0x84C1;
public const int GL_TEXTURE2 = 0x84C2;
public const int GL_TEXTURE3 = 0x84C3;
public const int GL_TEXTURE4 = 0x84C4;
public const int GL_TEXTURE5 = 0x84C5;
public const int GL_TEXTURE6 = 0x84C6;
public const int GL_TEXTURE7 = 0x84C7;
public const int GL_TEXTURE8 = 0x84C8;
public const int GL_TEXTURE9 = 0x84C9;
public const int GL_TEXTURE10 = 0x84CA;
public const int GL_TEXTURE11 = 0x84CB;
public const int GL_TEXTURE12 = 0x84CC;
public const int GL_TEXTURE13 = 0x84CD;
public const int GL_TEXTURE14 = 0x84CE;
public const int GL_TEXTURE15 = 0x84CF;
public const int GL_TEXTURE16 = 0x84D0;
public const int GL_TEXTURE17 = 0x84D1;
public const int GL_TEXTURE18 = 0x84D2;
public const int GL_TEXTURE19 = 0x84D3;
public const int GL_TEXTURE20 = 0x84D4;
public const int GL_TEXTURE21 = 0x84D5;
public const int GL_TEXTURE22 = 0x84D6;
public const int GL_TEXTURE23 = 0x84D7;
public const int GL_TEXTURE24 = 0x84D8;
public const int GL_TEXTURE25 = 0x84D9;
public const int GL_TEXTURE26 = 0x84DA;
public const int GL_TEXTURE27 = 0x84DB;
public const int GL_TEXTURE28 = 0x84DC;
public const int GL_TEXTURE29 = 0x84DD;
public const int GL_TEXTURE30 = 0x84DE;
public const int GL_TEXTURE31 = 0x84DF;
public const int GL_ACTIVE_TEXTURE = 0x84E0;
public const int GL_CLIENT_ACTIVE_TEXTURE = 0x84E1;
public const int GL_MAX_TEXTURE_UNITS = 0x84E2;
public const int GL_NORMAL_MAP = 0x8511;
public const int GL_REFLECTION_MAP = 0x8512;
public const int GL_TEXTURE_CUBE_MAP = 0x8513;
public const int GL_TEXTURE_BINDING_CUBE_MAP = 0x8514;
public const int GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515;
public const int GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516;
public const int GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517;
public const int GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518;
public const int GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519;
public const int GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A;
public const int GL_PROXY_TEXTURE_CUBE_MAP = 0x851B;
public const int GL_MAX_CUBE_MAP_TEXTURE_SIZE = 0x851C;
public const int GL_COMPRESSED_ALPHA = 0x84E9;
public const int GL_COMPRESSED_LUMINANCE = 0x84EA;
public const int GL_COMPRESSED_LUMINANCE_ALPHA = 0x84EB;
public const int GL_COMPRESSED_INTENSITY = 0x84EC;
public const int GL_COMPRESSED_RGB = 0x84ED;
public const int GL_COMPRESSED_RGBA = 0x84EE;
public const int GL_TEXTURE_COMPRESSION_HINT = 0x84EF;
public const int GL_TEXTURE_COMPRESSED_IMAGE_SIZE = 0x86A0;
public const int GL_TEXTURE_COMPRESSED = 0x86A1;
public const int GL_NUM_COMPRESSED_TEXTURE_FORMATS = 0x86A2;
public const int GL_COMPRESSED_TEXTURE_FORMATS = 0x86A3;
public const int GL_MULTISAMPLE = 0x809D;
public const int GL_SAMPLE_ALPHA_TO_COVERAGE = 0x809E;
public const int GL_SAMPLE_ALPHA_TO_ONE = 0x809F;
public const int GL_SAMPLE_COVERAGE = 0x80A0;
public const int GL_SAMPLE_BUFFERS = 0x80A8;
public const int GL_SAMPLES = 0x80A9;
public const int GL_SAMPLE_COVERAGE_VALUE = 0x80AA;
public const int GL_SAMPLE_COVERAGE_INVERT = 0x80AB;
public const int GL_MULTISAMPLE_BIT = 0x20000000;
public const int GL_TRANSPOSE_MODELVIEW_MATRIX = 0x84E3;
public const int GL_TRANSPOSE_PROJECTION_MATRIX = 0x84E4;
public const int GL_TRANSPOSE_TEXTURE_MATRIX = 0x84E5;
public const int GL_TRANSPOSE_COLOR_MATRIX = 0x84E6;
public const int GL_COMBINE = 0x8570;
public const int GL_COMBINE_RGB = 0x8571;
public const int GL_COMBINE_ALPHA = 0x8572;
public const int GL_SOURCE0_RGB = 0x8580;
public const int GL_SOURCE1_RGB = 0x8581;
public const int GL_SOURCE2_RGB = 0x8582;
public const int GL_SOURCE0_ALPHA = 0x8588;
public const int GL_SOURCE1_ALPHA = 0x8589;
public const int GL_SOURCE2_ALPHA = 0x858A;
public const int GL_OPERAND0_RGB = 0x8590;
public const int GL_OPERAND1_RGB = 0x8591;
public const int GL_OPERAND2_RGB = 0x8592;
public const int GL_OPERAND0_ALPHA = 0x8598;
public const int GL_OPERAND1_ALPHA = 0x8599;
public const int GL_OPERAND2_ALPHA = 0x859A;
public const int GL_RGB_SCALE = 0x8573;
public const int GL_ADD_SIGNED = 0x8574;
public const int GL_INTERPOLATE = 0x8575;
public const int GL_SUBTRACT = 0x84E7;
public const int GL_CONSTANT = 0x8576;
public const int GL_PRIMARY_COLOR = 0x8577;
public const int GL_PREVIOUS = 0x8578;
public const int GL_DOT3_RGB = 0x86AE;
public const int GL_DOT3_RGBA = 0x86AF;
public const int GL_CLAMP_TO_BORDER = 0x812D;
public const int GL_TEXTURE0_ARB = 0x84C0;
public const int GL_TEXTURE1_ARB = 0x84C1;
public const int GL_TEXTURE2_ARB = 0x84C2;
public const int GL_TEXTURE3_ARB = 0x84C3;
public const int GL_TEXTURE4_ARB = 0x84C4;
public const int GL_TEXTURE5_ARB = 0x84C5;
public const int GL_TEXTURE6_ARB = 0x84C6;
public const int GL_TEXTURE7_ARB = 0x84C7;
public const int GL_TEXTURE8_ARB = 0x84C8;
public const int GL_TEXTURE9_ARB = 0x84C9;
public const int GL_TEXTURE10_ARB = 0x84CA;
public const int GL_TEXTURE11_ARB = 0x84CB;
public const int GL_TEXTURE12_ARB = 0x84CC;
public const int GL_TEXTURE13_ARB = 0x84CD;
public const int GL_TEXTURE14_ARB = 0x84CE;
public const int GL_TEXTURE15_ARB = 0x84CF;
public const int GL_TEXTURE16_ARB = 0x84D0;
public const int GL_TEXTURE17_ARB = 0x84D1;
public const int GL_TEXTURE18_ARB = 0x84D2;
public const int GL_TEXTURE19_ARB = 0x84D3;
public const int GL_TEXTURE20_ARB = 0x84D4;
public const int GL_TEXTURE21_ARB = 0x84D5;
public const int GL_TEXTURE22_ARB = 0x84D6;
public const int GL_TEXTURE23_ARB = 0x84D7;
public const int GL_TEXTURE24_ARB = 0x84D8;
public const int GL_TEXTURE25_ARB = 0x84D9;
public const int GL_TEXTURE26_ARB = 0x84DA;
public const int GL_TEXTURE27_ARB = 0x84DB;
public const int GL_TEXTURE28_ARB = 0x84DC;
public const int GL_TEXTURE29_ARB = 0x84DD;
public const int GL_TEXTURE30_ARB = 0x84DE;
public const int GL_TEXTURE31_ARB = 0x84DF;
public const int GL_ACTIVE_TEXTURE_ARB = 0x84E0;
public const int GL_CLIENT_ACTIVE_TEXTURE_ARB = 0x84E1;
public const int GL_MAX_TEXTURE_UNITS_ARB = 0x84E2;
public const int GL_DEPTH_STENCIL_MESA = 0x8750;
public const int GL_UNSIGNED_INT_24_8_MESA = 0x8751;
public const int GL_UNSIGNED_INT_8_24_REV_MESA = 0x8752;
public const int GL_UNSIGNED_SHORT_15_1_MESA = 0x8753;
public const int GL_UNSIGNED_SHORT_1_15_REV_MESA = 0x8754;
public const int GL_ALPHA_BLEND_EQUATION_ATI = 0x883D;
// glext.h
public const int GL_FRAMEBUFFER_BINDING = 0x8CA6;
}
}

8
src/Avalonia.OpenGL/GlDisplayType.cs

@ -0,0 +1,8 @@
namespace Avalonia.OpenGL
{
public enum GlDisplayType
{
OpenGL2,
OpenGLES2
}
}

48
src/Avalonia.OpenGL/GlInterface.cs

@ -0,0 +1,48 @@
using System;
using System.Runtime.InteropServices;
namespace Avalonia.OpenGL
{
public delegate IntPtr GlGetProcAddressDelegate(string procName);
public class GlInterface : GlInterfaceBase
{
private readonly Func<string, bool, IntPtr> _getProcAddress;
public GlInterface(Func<string, bool, IntPtr> getProcAddress) : base(getProcAddress)
{
_getProcAddress = getProcAddress;
}
public IntPtr GetProcAddress(string proc) => _getProcAddress(proc, true);
public T GetProcAddress<T>(string proc) => Marshal.GetDelegateForFunctionPointer<T>(GetProcAddress(proc));
// ReSharper disable UnassignedGetOnlyAutoProperty
public delegate void GlClearStencil(int s);
[EntryPoint("glClearStencil")]
public GlClearStencil ClearStencil { get; }
public delegate void GlClearColor(int r, int g, int b, int a);
[EntryPoint("glClearColor")]
public GlClearColor ClearColor { get; }
public delegate void GlClear(int bits);
[EntryPoint("glClear")]
public GlClear Clear { get; }
public delegate void GlViewport(int x, int y, int width, int height);
[EntryPoint("glViewport")]
public GlViewport Viewport { get; }
[EntryPoint("glFlush")]
public Action Flush { get; }
public delegate void GlGetIntegerv(int name, out int rv);
[EntryPoint("glGetIntegerv")]
public GlGetIntegerv GetIntegerv { get; }
// ReSharper restore UnassignedGetOnlyAutoProperty
}
}

28
src/Avalonia.OpenGL/GlInterfaceBase.cs

@ -0,0 +1,28 @@
using System;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Avalonia.OpenGL
{
public class GlInterfaceBase
{
public GlInterfaceBase(Func<string, bool, IntPtr> getProcAddress)
{
foreach (var prop in this.GetType().GetProperties())
{
var a = prop.GetCustomAttribute<EntryPointAttribute>();
if (a != null)
{
var fieldName = $"<{prop.Name}>k__BackingField";
var field = prop.DeclaringType.GetField(fieldName,
BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null)
throw new InvalidProgramException($"Expected property {prop.Name} to have {fieldName}");
var proc = getProcAddress(a.EntryPoint, a.Optional);
if (proc != IntPtr.Zero)
field.SetValue(this, Marshal.GetDelegateForFunctionPointer(proc, prop.PropertyType));
}
}
}
}
}

8
src/Avalonia.OpenGL/IGlContext.cs

@ -0,0 +1,8 @@
namespace Avalonia.OpenGL
{
public interface IGlContext
{
IGlDisplay Display { get; }
void MakeCurrent(IGlSurface surface);
}
}

11
src/Avalonia.OpenGL/IGlDisplay.cs

@ -0,0 +1,11 @@
namespace Avalonia.OpenGL
{
public interface IGlDisplay
{
GlDisplayType Type { get; }
GlInterface GlInterface { get; }
void ClearContext();
int SampleCount { get; }
int StencilSize { get; }
}
}

7
src/Avalonia.OpenGL/IGlPlatformSurface.cs

@ -0,0 +1,7 @@
namespace Avalonia.OpenGL
{
public interface IGlPlatformSurface
{
IGlPlatformSurfaceRenderTarget CreateGlRenderTarget();
}
}

9
src/Avalonia.OpenGL/IGlPlatformSurfaceRenderTarget.cs

@ -0,0 +1,9 @@
using System;
namespace Avalonia.OpenGL
{
public interface IGlPlatformSurfaceRenderTarget : IDisposable
{
IGlPlatformSurfaceRenderingSession BeginDraw();
}
}

12
src/Avalonia.OpenGL/IGlPlatformSurfaceRenderingSession.cs

@ -0,0 +1,12 @@
using System;
namespace Avalonia.OpenGL
{
public interface IGlPlatformSurfaceRenderingSession : IDisposable
{
IGlDisplay Display { get; }
// TODO: Change to PixelSize struct once https://github.com/AvaloniaUI/Avalonia/pull/1889 is merged
System.Drawing.Size PixelSize { get; }
double Scaling { get; }
}
}

10
src/Avalonia.OpenGL/IGlSurface.cs

@ -0,0 +1,10 @@
using System;
namespace Avalonia.OpenGL
{
public interface IGlSurface : IDisposable
{
IGlDisplay Display { get; }
void SwapBuffers();
}
}

7
src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs

@ -0,0 +1,7 @@
namespace Avalonia.OpenGL
{
public interface IWindowingPlatformGlFeature
{
IGlContext ImmediateContext { get; }
}
}

12
src/Avalonia.OpenGL/OpenGlException.cs

@ -0,0 +1,12 @@
using System;
namespace Avalonia.OpenGL
{
public class OpenGlException : Exception
{
public OpenGlException(string message) : base(message)
{
}
}
}

4
src/Avalonia.ReactiveUI/AppBuilderExtensions.cs

@ -4,6 +4,7 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Threading; using Avalonia.Threading;
using ReactiveUI; using ReactiveUI;
using Splat;
namespace Avalonia namespace Avalonia
{ {
@ -15,6 +16,9 @@ namespace Avalonia
return builder.AfterSetup(_ => return builder.AfterSetup(_ =>
{ {
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance; RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
Locator.CurrentMutable.Register(
() => new AvaloniaActivationForViewFetcher(),
typeof(IActivationForViewFetcher));
}); });
} }
} }

38
src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs

@ -0,0 +1,38 @@
// 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.Reflection;
using System.Reactive.Linq;
using Avalonia;
using Avalonia.VisualTree;
using ReactiveUI;
namespace Avalonia
{
public class AvaloniaActivationForViewFetcher : IActivationForViewFetcher
{
public int GetAffinityForView(Type view)
{
return typeof(IVisual).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) ? 10 : 0;
}
public IObservable<bool> GetActivationForView(IActivatable view)
{
if (!(view is IVisual visual)) return Observable.Return(false);
var viewLoaded = Observable
.FromEventPattern<VisualTreeAttachmentEventArgs>(
x => visual.AttachedToVisualTree += x,
x => visual.DetachedFromVisualTree -= x)
.Select(args => true);
var viewUnloaded = Observable
.FromEventPattern<VisualTreeAttachmentEventArgs>(
x => visual.DetachedFromVisualTree += x,
x => visual.DetachedFromVisualTree -= x)
.Select(args => false);
return viewLoaded
.Merge(viewUnloaded)
.DistinctUntilChanged();
}
}
}

19
src/Avalonia.Styling/Styling/Style.cs

@ -125,17 +125,20 @@ namespace Avalonia.Styling
var subs = new CompositeDisposable(Setters.Count + Animations.Count); var subs = new CompositeDisposable(Setters.Count + Animations.Count);
foreach (var animation in Animations) if (control is Animatable animatable)
{ {
IObservable<bool> obsMatch = match.ObservableResult; foreach (var animation in Animations)
if (match.ImmediateResult == true)
{ {
obsMatch = Observable.Return(true); IObservable<bool> obsMatch = match.ObservableResult;
}
var sub = animation.Apply((Animatable)control, obsMatch); if (match.ImmediateResult == true)
subs.Add(sub); {
obsMatch = Observable.Return(true);
}
var sub = animation.Apply(animatable, null, obsMatch);
subs.Add(sub);
}
} }
foreach (var setter in Setters) foreach (var setter in Setters)

12
src/Avalonia.Themes.Default/MenuItem.xaml

@ -73,7 +73,17 @@
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>
</Style> </Style>
<Style Selector="MenuItem:separator">
<Setter Property="Template">
<ControlTemplate>
<Separator Background="{DynamicResource ThemeControlMidBrush}"
Margin="29,1,0,1"
Height="1"/>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="Menu > MenuItem"> <Style Selector="Menu > MenuItem">
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>

26
src/Avalonia.Visuals/Animation/RenderLoopClock.cs

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Rendering;
namespace Avalonia.Animation
{
public class RenderLoopClock : ClockBase, IRenderLoopTask, IGlobalClock
{
protected override void Stop()
{
AvaloniaLocator.Current.GetService<IRenderLoop>().Remove(this);
}
bool IRenderLoopTask.NeedsUpdate => HasSubscriptions;
void IRenderLoopTask.Render()
{
}
void IRenderLoopTask.Update(TimeSpan time)
{
Pulse(time);
}
}
}

20
src/Avalonia.Visuals/Animation/TransformAnimator.cs

@ -9,10 +9,10 @@ namespace Avalonia.Animation
/// </summary> /// </summary>
public class TransformAnimator : Animator<double> public class TransformAnimator : Animator<double>
{ {
DoubleAnimator childKeyFrames; DoubleAnimator childAnimator;
/// <inheritdoc/> /// <inheritdoc/>
public override IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch, Action onComplete) public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> obsMatch, Action onComplete)
{ {
var ctrl = (Visual)control; var ctrl = (Visual)control;
@ -36,15 +36,15 @@ namespace Avalonia.Animation
var renderTransformType = ctrl.RenderTransform.GetType(); var renderTransformType = ctrl.RenderTransform.GetType();
if (childKeyFrames == null) if (childAnimator == null)
{ {
InitializeChildKeyFrames(); InitializeChildAnimator();
} }
// It's a transform object so let's target that. // It's a transform object so let's target that.
if (renderTransformType == Property.OwnerType) if (renderTransformType == Property.OwnerType)
{ {
return childKeyFrames.Apply(animation, ctrl.RenderTransform, obsMatch, onComplete); return childAnimator.Apply(animation, ctrl.RenderTransform, clock ?? control.Clock, obsMatch, onComplete);
} }
// It's a TransformGroup and try finding the target there. // It's a TransformGroup and try finding the target there.
else if (renderTransformType == typeof(TransformGroup)) else if (renderTransformType == typeof(TransformGroup))
@ -53,7 +53,7 @@ namespace Avalonia.Animation
{ {
if (transform.GetType() == Property.OwnerType) if (transform.GetType() == Property.OwnerType)
{ {
return childKeyFrames.Apply(animation, transform, obsMatch, onComplete); return childAnimator.Apply(animation, transform, clock ?? control.Clock, obsMatch, onComplete);
} }
} }
} }
@ -73,16 +73,16 @@ namespace Avalonia.Animation
return null; return null;
} }
void InitializeChildKeyFrames() void InitializeChildAnimator()
{ {
childKeyFrames = new DoubleAnimator(); childAnimator = new DoubleAnimator();
foreach (AnimatorKeyFrame keyframe in this) foreach (AnimatorKeyFrame keyframe in this)
{ {
childKeyFrames.Add(keyframe); childAnimator.Add(keyframe);
} }
childKeyFrames.Property = Property; childAnimator.Property = Property;
} }
/// <inheritdocs/> /// <inheritdocs/>

1
src/Avalonia.Visuals/Avalonia.Visuals.csproj

@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Avalonia</RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" /> <ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />

2
src/Avalonia.Visuals/Matrix.cs

@ -47,7 +47,7 @@ namespace Avalonia
/// <summary> /// <summary>
/// Returns the multiplicative identity matrix. /// Returns the multiplicative identity matrix.
/// </summary> /// </summary>
public static Matrix Identity => new Matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); public static Matrix Identity { get; } = new Matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
/// <summary> /// <summary>
/// Returns whether the matrix is the identity matrix. /// Returns whether the matrix is the identity matrix.

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

Loading…
Cancel
Save