-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added support for WinUI3 desktop (#2897)
- Loading branch information
1 parent
43fb50d
commit 66a4d38
Showing
10 changed files
with
719 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for full license information. | ||
|
||
using System; | ||
using System.Linq; | ||
using System.Reactive.Linq; | ||
using System.Reflection; | ||
using Microsoft.UI.Xaml; | ||
using Windows.Foundation; | ||
|
||
namespace ReactiveUI.WinUI | ||
{ | ||
/// <summary> | ||
/// ActiveationForViewFetcher is how ReactiveUI determine when a | ||
/// View is activated or deactivated. This is usually only used when porting | ||
/// ReactiveUI to a new UI framework. | ||
/// </summary> | ||
public class ActivationForViewFetcher : IActivationForViewFetcher | ||
{ | ||
/// <inheritdoc/> | ||
public int GetAffinityForView(Type view) => typeof(FrameworkElement).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) ? 10 : 0; | ||
|
||
/// <inheritdoc/> | ||
public IObservable<bool> GetActivationForView(IActivatableView view) | ||
{ | ||
if (!(view is FrameworkElement fe)) | ||
{ | ||
return Observable<bool>.Empty; | ||
} | ||
|
||
var viewLoaded = Observable.FromEvent<TypedEventHandler<FrameworkElement, object>, bool>( | ||
eventHandler => | ||
{ | ||
void Handler(FrameworkElement sender, object e) => eventHandler(true); | ||
return Handler; | ||
}, | ||
x => fe.Loading += x, | ||
x => fe.Loading -= x); | ||
|
||
var viewUnloaded = Observable.FromEvent<RoutedEventHandler, bool>( | ||
eventHandler => | ||
{ | ||
void Handler(object sender, RoutedEventArgs e) => eventHandler(false); | ||
return Handler; | ||
}, | ||
x => fe.Unloaded += x, | ||
x => fe.Unloaded -= x); | ||
|
||
return viewLoaded | ||
.Merge(viewUnloaded) | ||
.Select(b => b ? fe.WhenAnyValue(x => x.IsHitTestVisible).SkipWhile(x => !x) : Observables.False) | ||
.Switch() | ||
.DistinctUntilChanged(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT License. | ||
// See the LICENSE file in the project root for more information. | ||
// <auto-generated /> | ||
|
||
#if HAS_WINUI | ||
using System.Reactive.Disposables; | ||
using System.Threading; | ||
using Microsoft.UI.Dispatching; | ||
|
||
namespace System.Reactive.Concurrency | ||
{ | ||
/// <summary> | ||
/// Represents an object that schedules units of work on a <see cref="System.Windows.Threading.Dispatcher"/>. | ||
/// </summary> | ||
/// <remarks> | ||
/// This scheduler type is typically used indirectly through the <see cref="Linq.DispatcherObservable.ObserveOnDispatcher{TSource}(IObservable{TSource})"/> and <see cref="Linq.DispatcherObservable.SubscribeOnDispatcher{TSource}(IObservable{TSource})"/> methods that use the Dispatcher on the calling thread. | ||
/// </remarks> | ||
public class DispatcherQueueScheduler : LocalScheduler, ISchedulerPeriodic | ||
{ | ||
/// <summary> | ||
/// Gets the scheduler that schedules work on the <see cref="System.Windows.Threading.Dispatcher"/> for the current thread. | ||
/// </summary> | ||
public static DispatcherQueueScheduler Current | ||
{ | ||
get | ||
{ | ||
var dispatcher = DispatcherQueue.GetForCurrentThread(); | ||
if (dispatcher == null) | ||
{ | ||
throw new InvalidOperationException("There is no current dispatcher thread"); | ||
} | ||
|
||
return new DispatcherQueueScheduler(dispatcher); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Constructs a <see cref="DispatcherScheduler"/> that schedules units of work on the given <see cref="System.Windows.Threading.Dispatcher"/>. | ||
/// </summary> | ||
/// <param name="dispatcher"><see cref="DispatcherScheduler"/> to schedule work on.</param> | ||
/// <exception cref="ArgumentNullException"><paramref name="dispatcher"/> is <c>null</c>.</exception> | ||
public DispatcherQueueScheduler(DispatcherQueue dispatcher) | ||
{ | ||
Dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); | ||
Priority = DispatcherQueuePriority.Normal; | ||
|
||
} | ||
|
||
/// <summary> | ||
/// Constructs a <see cref="DispatcherScheduler"/> that schedules units of work on the given <see cref="System.Windows.Threading.Dispatcher"/> at the given priority. | ||
/// </summary> | ||
/// <param name="dispatcher"><see cref="DispatcherScheduler"/> to schedule work on.</param> | ||
/// <param name="priority">Priority at which units of work are scheduled.</param> | ||
/// <exception cref="ArgumentNullException"><paramref name="dispatcher"/> is <c>null</c>.</exception> | ||
public DispatcherQueueScheduler(DispatcherQueue dispatcher, DispatcherQueuePriority priority) | ||
{ | ||
Dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); | ||
Priority = priority; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the <see cref="System.Windows.Threading.Dispatcher"/> associated with the <see cref="DispatcherScheduler"/>. | ||
/// </summary> | ||
public DispatcherQueue Dispatcher { get; } | ||
|
||
/// <summary> | ||
/// Gets the priority at which work items will be dispatched. | ||
/// </summary> | ||
public DispatcherQueuePriority Priority { get; } | ||
|
||
/// <summary> | ||
/// Schedules an action to be executed on the dispatcher. | ||
/// </summary> | ||
/// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam> | ||
/// <param name="state">State passed to the action to be executed.</param> | ||
/// <param name="action">Action to be executed.</param> | ||
/// <returns>The disposable object used to cancel the scheduled action (best effort).</returns> | ||
/// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception> | ||
public override IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action) | ||
{ | ||
if (action == null) | ||
{ | ||
throw new ArgumentNullException(nameof(action)); | ||
} | ||
|
||
var d = new SingleAssignmentDisposable(); | ||
|
||
Dispatcher.TryEnqueue(Priority, | ||
() => | ||
{ | ||
if (!d.IsDisposed) | ||
{ | ||
d.Disposable = action(this, state); | ||
} | ||
}); | ||
|
||
return d; | ||
} | ||
|
||
/// <summary> | ||
/// Schedules an action to be executed after <paramref name="dueTime"/> on the dispatcher, using a <see cref="System.Windows.Threading.DispatcherTimer"/> object. | ||
/// </summary> | ||
/// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam> | ||
/// <param name="state">State passed to the action to be executed.</param> | ||
/// <param name="action">Action to be executed.</param> | ||
/// <param name="dueTime">Relative time after which to execute the action.</param> | ||
/// <returns>The disposable object used to cancel the scheduled action (best effort).</returns> | ||
/// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception> | ||
public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action) | ||
{ | ||
if (action == null) | ||
{ | ||
throw new ArgumentNullException(nameof(action)); | ||
} | ||
|
||
var dt = Scheduler.Normalize(dueTime); | ||
if (dt.Ticks == 0) | ||
{ | ||
return Schedule(state, action); | ||
} | ||
|
||
return ScheduleSlow(state, dt, action); | ||
} | ||
|
||
private IDisposable ScheduleSlow<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action) | ||
{ | ||
var d = new MultipleAssignmentDisposable(); | ||
|
||
var timer = Dispatcher.CreateTimer(); | ||
|
||
timer.Tick += (s, e) => | ||
{ | ||
var t = Interlocked.Exchange(ref timer, null); | ||
if (t != null) | ||
{ | ||
try | ||
{ | ||
d.Disposable = action(this, state); | ||
} | ||
finally | ||
{ | ||
t.Stop(); | ||
action = static (s, t) => Disposable.Empty; | ||
} | ||
} | ||
}; | ||
|
||
timer.Interval = dueTime; | ||
timer.Start(); | ||
|
||
d.Disposable = Disposable.Create(() => | ||
{ | ||
var t = Interlocked.Exchange(ref timer, null); | ||
if (t != null) | ||
{ | ||
t.Stop(); | ||
action = static (s, t) => Disposable.Empty; | ||
} | ||
}); | ||
|
||
return d; | ||
} | ||
|
||
/// <summary> | ||
/// Schedules a periodic piece of work on the dispatcher, using a <see cref="System.Windows.Threading.DispatcherTimer"/> object. | ||
/// </summary> | ||
/// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam> | ||
/// <param name="state">Initial state passed to the action upon the first iteration.</param> | ||
/// <param name="period">Period for running the work periodically.</param> | ||
/// <param name="action">Action to be executed, potentially updating the state.</param> | ||
/// <returns>The disposable object used to cancel the scheduled recurring action (best effort).</returns> | ||
/// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception> | ||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="period"/> is less than <see cref="TimeSpan.Zero"/>.</exception> | ||
public IDisposable SchedulePeriodic<TState>(TState state, TimeSpan period, Func<TState, TState> action) | ||
{ | ||
if (period < TimeSpan.Zero) | ||
{ | ||
throw new ArgumentOutOfRangeException(nameof(period)); | ||
} | ||
|
||
if (action == null) | ||
{ | ||
throw new ArgumentNullException(nameof(action)); | ||
} | ||
|
||
var timer = Dispatcher.CreateTimer(); | ||
|
||
var state1 = state; | ||
|
||
timer.Tick += (s, e) => | ||
{ | ||
state1 = action(state1); | ||
}; | ||
|
||
timer.Interval = period; | ||
timer.Start(); | ||
|
||
return Disposable.Create(() => | ||
{ | ||
var t = Interlocked.Exchange(ref timer, null); | ||
if (t != null) | ||
{ | ||
t.Stop(); | ||
action = static _ => _; | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<TargetFramework>net5.0-windows10.0.19041.0</TargetFramework> | ||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> | ||
<PackageDescription>Contains the ReactiveUI platform specific extensions for WinUI Desktop</PackageDescription> | ||
<RootNamespace>ReactiveUI.WinUI.Desktop</RootNamespace> | ||
<RuntimeIdentifiers>win10-x86;win10-x64</RuntimeIdentifiers> | ||
<DefineConstants>HAS_WINUI</DefineConstants> | ||
<PackageTags>mvvm;reactiveui;rx;reactive extensions;observable;LINQ;events;winui</PackageTags> | ||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||
<UseWinUI>true</UseWinUI> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.ProjectReunion" Version="0.8.2" /> | ||
<PackageReference Include="Microsoft.ProjectReunion.Foundation" Version="0.8.2" /> | ||
<PackageReference Include="Microsoft.ProjectReunion.WinUI" Version="0.8.2" /> | ||
<PackageReference Include="System.Reactive" Version="5.0.0" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\ReactiveUI\ReactiveUI.csproj" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Compile Include="..\ReactiveUI.Uwp\common\**\*.cs" LinkBase="common" /> | ||
<None Include="..\ReactiveUI.Uwp\Rx\**\*.cs" LinkBase="Rx" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Oops, something went wrong.