Skip to content

Commit

Permalink
R3.Avalonia
Browse files Browse the repository at this point in the history
  • Loading branch information
neuecc committed Jan 6, 2024
1 parent ea90e36 commit aba9ec1
Show file tree
Hide file tree
Showing 17 changed files with 727 additions and 8 deletions.
16 changes: 15 additions & 1 deletion R3.sln
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{C7327A31-4
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "R3.WPF", "src\R3.WPF\R3.WPF.csproj", "{57AC0130-0D5F-489A-A565-B41EF9928085}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfApp1", "sandbox\WpfApp1\WpfApp1.csproj", "{BA40E541-3BCD-438A-B966-2FC7BE80AB80}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfApp1", "sandbox\WpfApp1\WpfApp1.csproj", "{BA40E541-3BCD-438A-B966-2FC7BE80AB80}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "R3.Avalonia", "src\R3.Avalonia\R3.Avalonia.csproj", "{C492C048-732F-4F0D-AC09-03F3187ABB17}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvaloniaApplication1", "sandbox\AvaloniaApplication1\AvaloniaApplication1.csproj", "{978BECEF-5217-4531-B0B7-45BC8D820A28}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -52,6 +56,14 @@ Global
{BA40E541-3BCD-438A-B966-2FC7BE80AB80}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BA40E541-3BCD-438A-B966-2FC7BE80AB80}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BA40E541-3BCD-438A-B966-2FC7BE80AB80}.Release|Any CPU.Build.0 = Release|Any CPU
{C492C048-732F-4F0D-AC09-03F3187ABB17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C492C048-732F-4F0D-AC09-03F3187ABB17}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C492C048-732F-4F0D-AC09-03F3187ABB17}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C492C048-732F-4F0D-AC09-03F3187ABB17}.Release|Any CPU.Build.0 = Release|Any CPU
{978BECEF-5217-4531-B0B7-45BC8D820A28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{978BECEF-5217-4531-B0B7-45BC8D820A28}.Debug|Any CPU.Build.0 = Debug|Any CPU
{978BECEF-5217-4531-B0B7-45BC8D820A28}.Release|Any CPU.ActiveCfg = Release|Any CPU
{978BECEF-5217-4531-B0B7-45BC8D820A28}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -62,6 +74,8 @@ Global
{42F7C4F7-3BB3-4DC8-8285-9DFF7E93BC4B} = {0544806B-3BB4-43CF-8277-BC612F32208D}
{57AC0130-0D5F-489A-A565-B41EF9928085} = {9FA6D327-728B-4436-AE3A-9E46D8FEF591}
{BA40E541-3BCD-438A-B966-2FC7BE80AB80} = {FAB2137C-1DBA-4F2F-8E22-DF3521C9B365}
{C492C048-732F-4F0D-AC09-03F3187ABB17} = {9FA6D327-728B-4436-AE3A-9E46D8FEF591}
{978BECEF-5217-4531-B0B7-45BC8D820A28} = {FAB2137C-1DBA-4F2F-8E22-DF3521C9B365}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {84B77761-6B9E-46BA-B132-6C77B0B6E4FA}
Expand Down
10 changes: 10 additions & 0 deletions sandbox/AvaloniaApplication1/App.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApplication1.App"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->

<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>
22 changes: 22 additions & 0 deletions sandbox/AvaloniaApplication1/App.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;

namespace AvaloniaApplication1;
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}

public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow();
}

base.OnFrameworkInitializationCompleted();
}
}
27 changes: 27 additions & 0 deletions sandbox/AvaloniaApplication1/AvaloniaApplication1.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<IsPackable>false</IsPackable>
</PropertyGroup>


<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.6" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.6" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.6" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.0.6" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.6" />
</ItemGroup>


<ItemGroup>
<ProjectReference Include="..\..\src\R3.Avalonia\R3.Avalonia.csproj" />
<ProjectReference Include="..\..\src\R3\R3.csproj" />
</ItemGroup>
</Project>
11 changes: 11 additions & 0 deletions sandbox/AvaloniaApplication1/MainWindow.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AvaloniaApplication1.MainWindow"
Title="AvaloniaApplication1">

<TextBlock Name="textBlock" />

</Window>
54 changes: 54 additions & 0 deletions sandbox/AvaloniaApplication1/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using Avalonia.Controls;
using R3;
using System;
using System.Diagnostics;

namespace AvaloniaApplication1;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();


textBlock.Text = "Hello World";







// Observable.EveryValueChanged(this, x => x.Width).Subscribe(x => textBlock.Text = x.ToString());
// this.ObserveEveryValueChanged(x => x.Height).Subscribe(x => HeightText.Text = x.ToString());

var sw = Stopwatch.StartNew();

//System.Reactive.Linq.Observable.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)).Subscribe(_ =>
//{
// textBlock.Text = "Hello World:" + sw.Elapsed;
//});
//Observable.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5))
//// // .ObserveOnCurrentDispatcher()
// .Subscribe(_ =>
// {
// // textBlock.Text = "Hello World:" + sw.Elapsed;
// });

//Observable.TimerFrame(50, 100).Subscribe(_ =>
//{
// textBlock.Text = "Hello World:" + ObservableSystem.DefaultFrameProvider.GetFrameCount();
//});



Observable.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5), TimeProvider.System)
.ObserveOnUIThreadDispatcher()
.Subscribe(_ =>
{
textBlock.Text = "Hello World:" + sw.Elapsed;
});


}
}
22 changes: 22 additions & 0 deletions sandbox/AvaloniaApplication1/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Avalonia;
using System;

namespace AvaloniaApplication1;

internal class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);

// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace()
.UseR3(); // add this line
}
18 changes: 18 additions & 0 deletions sandbox/AvaloniaApplication1/app.manifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="AvaloniaApplication1.Desktop"/>

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->

<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>
4 changes: 2 additions & 2 deletions sandbox/WpfApp1/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ public MainWindow()
//{
// textBlock.Text = "Hello World:" + sw.Elapsed;
//});
Observable.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5), TimeProvider.System)
.ObserveOnCurrentDispatcher()
Observable.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5))
// // .ObserveOnCurrentDispatcher()
.Subscribe(_ =>
{
textBlock.Text = "Hello World:" + sw.Elapsed;
Expand Down
1 change: 1 addition & 0 deletions sandbox/WpfApp1/WpfApp1.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
Expand Down
28 changes: 28 additions & 0 deletions src/R3.Avalonia/AppBuilderR3InitializeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Avalonia.Threading;
using R3.Avalonia;

namespace Avalonia; // Avalonia namespace

public static class AppBuilderR3InitializeExtensions
{
public static AppBuilder UseR3(this AppBuilder builder)
{
// need to delay setup, initialize provider(dispatcher) need to determine platform
return builder.AfterSetup(_ => AvaloniaProviderInitializer.SetDefaultProviders());
}

public static AppBuilder UseR3(this AppBuilder builder, DispatcherPriority priority)
{
return builder.AfterSetup(_ => AvaloniaProviderInitializer.SetDefaultProviders(priority));
}

public static AppBuilder UseR3(this AppBuilder builder, int framesPerSecond)
{
return builder.AfterSetup(_ => AvaloniaProviderInitializer.SetDefaultProviders(framesPerSecond));
}

public static AppBuilder UseR3(this AppBuilder builder, DispatcherPriority priority, int framesPerSecond)
{
return builder.AfterSetup(_ => AvaloniaProviderInitializer.SetDefaultProviders(priority, framesPerSecond));
}
}
115 changes: 115 additions & 0 deletions src/R3.Avalonia/AvaloniaDispatcherFrameProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using Avalonia.Threading;
using R3.Collections;
using System.Diagnostics.CodeAnalysis;

namespace R3.Avalonia;

// like the Avalonia.Rendering.UiThreadRenderTimer

// NOTE: idially, not polling, use like the WPF's CompositionTarget.Rendering

public sealed class AvaloniaDispatcherFrameProvider : FrameProvider
{
bool disposed;
long frameCount;
FreeListCore<IFrameRunnerWorkItem> list;
readonly object gate = new object();
readonly DispatcherTimer timer;
EventHandler timerTick;

// frame loop is delayed until first register
bool running;

public AvaloniaDispatcherFrameProvider()
: this(60, null)
{
}

public AvaloniaDispatcherFrameProvider(int framesPerSecond)
: this(framesPerSecond, null)
{
}

public AvaloniaDispatcherFrameProvider(int framesPerSecond, DispatcherPriority? dispatcherPriority)
{
this.timerTick = Run;
this.list = new FreeListCore<IFrameRunnerWorkItem>(gate);
this.timer = dispatcherPriority == null
? new DispatcherTimer()
: new DispatcherTimer(dispatcherPriority.Value);
this.timer.Interval = TimeSpan.FromSeconds(1.0 / framesPerSecond);
this.timer.Tick += timerTick;
}

public override long GetFrameCount()
{
ThrowObjectDisposedIf(disposed, typeof(NewThreadSleepFrameProvider));
return frameCount;
}

public override void Register(IFrameRunnerWorkItem callback)
{
ThrowObjectDisposedIf(disposed, typeof(NewThreadSleepFrameProvider));
lock (gate)
{
if (running == false)
{
running = true;
timer.Start();
}
list.Add(callback, out _);
}
}

public void Dispose()
{
lock (gate)
{
disposed = true;
this.timer.Tick -= timerTick;
this.timer.Stop();
list.Dispose();
}
}

void Run(object? sender, EventArgs e)
{
frameCount++;

var span = list.AsSpan();
for (int i = 0; i < span.Length; i++)
{
ref readonly var item = ref span[i];
if (item != null)
{
try
{
if (!item.MoveNext(frameCount))
{
list.Remove(i);
}
}
catch (Exception ex)
{
list.Remove(i);
try
{
ObservableSystem.GetUnhandledExceptionHandler().Invoke(ex);
}
catch { }
}
}
}
}

static void ThrowObjectDisposedIf([DoesNotReturnIf(true)] bool condition, Type type)
{
if (condition)
{
ThrowObjectDisposedException(type);
}
}

[DoesNotReturn]
internal static void ThrowObjectDisposedException(Type? type) => throw new ObjectDisposedException(type?.FullName);
}
Loading

0 comments on commit aba9ec1

Please sign in to comment.