forked from reactiveui/ReactiveUI
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: Added Uno support (reactiveui#2067)
- Loading branch information
1 parent
0e0a1e7
commit 5fdbd63
Showing
21 changed files
with
603 additions
and
31 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
1 change: 1 addition & 0 deletions
1
src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt
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
1 change: 1 addition & 0 deletions
1
src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp2.0.approved.txt
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,66 @@ | ||
// Copyright (c) 2019 .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; | ||
using System.Reactive.Linq; | ||
using System.Reflection; | ||
|
||
using Windows.Foundation; | ||
using Windows.UI.Xaml; | ||
|
||
namespace ReactiveUI | ||
{ | ||
/// <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) | ||
{ | ||
return typeof(FrameworkElement).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) ? 10 : 0; | ||
} | ||
|
||
/// <inheritdoc/> | ||
public IObservable<bool> GetActivationForView(IActivatable view) | ||
{ | ||
var fe = view as FrameworkElement; | ||
|
||
if (fe == null) | ||
{ | ||
return Observable<bool>.Empty; | ||
} | ||
|
||
#pragma warning disable SA1114 // Parameter list after. | ||
#if NETSTANDARD || MAC | ||
var viewLoaded = Observable.FromEvent<RoutedEventHandler, bool>( | ||
#else | ||
var viewLoaded = Observable.FromEvent<TypedEventHandler<DependencyObject, object>, bool>( | ||
#endif | ||
eventHandler => (_, __) => eventHandler(true), | ||
x => fe.Loading += x, | ||
x => fe.Loading -= x); | ||
var viewUnloaded = Observable.FromEvent<RoutedEventHandler, bool>( | ||
handler => | ||
{ | ||
void EventHandler(object sender, RoutedEventArgs e) => handler(false); | ||
return EventHandler; | ||
}, | ||
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,251 @@ | ||
// Copyright (c) 2019 .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. | ||
|
||
// <auto-generated /> | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Reactive.Disposables; | ||
using System.Runtime.ExceptionServices; | ||
using System.Text; | ||
using System.Threading; | ||
|
||
using Windows.UI.Core; | ||
using Windows.UI.Xaml; | ||
|
||
namespace System.Reactive.Concurrency | ||
{ | ||
/// <summary> | ||
/// Represents an object that schedules units of work on a <see cref="CoreDispatcher"/>. | ||
/// </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 current Dispatcher. | ||
/// </remarks> | ||
[CLSCompliant(false)] | ||
public sealed class CoreDispatcherScheduler : LocalScheduler, ISchedulerPeriodic | ||
{ | ||
/// <summary> | ||
/// Constructs a <see cref="CoreDispatcherScheduler"/> that schedules units of work on the given <see cref="CoreDispatcher"/>. | ||
/// </summary> | ||
/// <param name="dispatcher">Dispatcher to schedule work on.</param> | ||
/// <exception cref="ArgumentNullException"><paramref name="dispatcher"/> is <c>null</c>.</exception> | ||
public CoreDispatcherScheduler(CoreDispatcher dispatcher) | ||
{ | ||
Dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); | ||
Priority = CoreDispatcherPriority.Normal; | ||
} | ||
|
||
/// <summary> | ||
/// Constructs a <see cref="CoreDispatcherScheduler"/> that schedules units of work on the given <see cref="CoreDispatcher"/> with the given priority. | ||
/// </summary> | ||
/// <param name="dispatcher">Dispatcher to schedule work on.</param> | ||
/// <param name="priority">Priority for scheduled units of work.</param> | ||
/// <exception cref="ArgumentNullException"><paramref name="dispatcher"/> is <c>null</c>.</exception> | ||
public CoreDispatcherScheduler(CoreDispatcher dispatcher, CoreDispatcherPriority priority) | ||
{ | ||
Dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); | ||
Priority = priority; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the scheduler that schedules work on the <see cref="CoreDispatcher"/> associated with the current Window. | ||
/// </summary> | ||
public static CoreDispatcherScheduler Current | ||
{ | ||
get | ||
{ | ||
var window = Window.Current; | ||
if (window == null) | ||
{ | ||
throw new InvalidOperationException("There is no current window that has been created."); | ||
} | ||
|
||
return new CoreDispatcherScheduler(window.Dispatcher); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Gets the <see cref="CoreDispatcher"/> associated with the <see cref="CoreDispatcherScheduler"/>. | ||
/// </summary> | ||
public CoreDispatcher Dispatcher { get; } | ||
|
||
/// <summary> | ||
/// Gets the priority at which work is scheduled. | ||
/// </summary> | ||
public CoreDispatcherPriority 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(); | ||
|
||
var res = Dispatcher.RunAsync(Priority, () => | ||
{ | ||
if (!d.IsDisposed) | ||
{ | ||
try | ||
{ | ||
d.Disposable = action(this, state); | ||
} | ||
catch (Exception ex) | ||
{ | ||
// | ||
// Work-around for the behavior of throwing from RunAsync not propagating | ||
// the exception to the Application.UnhandledException event (as of W8RP) | ||
// as our users have come to expect from previous XAML stacks using Rx. | ||
// | ||
// If we wouldn't do this, there'd be an observable behavioral difference | ||
// between scheduling with TimeSpan.Zero or using this overload. | ||
// | ||
// For scheduler implementation guidance rules, see TaskPoolScheduler.cs | ||
// in System.Reactive.PlatformServices\Reactive\Concurrency. | ||
// | ||
var timer = new DispatcherTimer | ||
{ | ||
Interval = TimeSpan.Zero | ||
}; | ||
timer.Tick += (o, e) => | ||
{ | ||
timer.Stop(); | ||
ExceptionDispatchInfo.Capture(ex).Throw(); | ||
}; | ||
timer.Start(); | ||
} | ||
} | ||
}); | ||
|
||
return StableCompositeDisposable.Create( | ||
d, | ||
Disposable.Create(res, _ => _.Cancel()) | ||
); | ||
} | ||
|
||
/// <summary> | ||
/// Schedules an action to be executed after <paramref name="dueTime"/> on the dispatcher, using a <see cref="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 = new DispatcherTimer(); | ||
|
||
timer.Tick += (o, e) => | ||
{ | ||
var t = Interlocked.Exchange(ref timer, null); | ||
if (t != null) | ||
{ | ||
try | ||
{ | ||
d.Disposable = action(this, state); | ||
} | ||
finally | ||
{ | ||
t.Stop(); | ||
action = null; | ||
} | ||
} | ||
}; | ||
|
||
timer.Interval = dueTime; | ||
timer.Start(); | ||
|
||
d.Disposable = Disposable.Create(() => | ||
{ | ||
var t = Interlocked.Exchange(ref timer, null); | ||
if (t != null) | ||
{ | ||
t.Stop(); | ||
action = (_, __) => Disposable.Empty; | ||
} | ||
}); | ||
|
||
return d; | ||
} | ||
|
||
/// <summary> | ||
/// Schedules a periodic piece of work on the dispatcher, using a <see cref="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) | ||
{ | ||
// | ||
// According to MSDN documentation, the default is TimeSpan.Zero, so that's definitely valid. | ||
// Empirical observation - negative values seem to be normalized to TimeSpan.Zero, but let's not go there. | ||
// | ||
if (period < TimeSpan.Zero) | ||
{ | ||
throw new ArgumentOutOfRangeException(nameof(period)); | ||
} | ||
|
||
if (action == null) | ||
{ | ||
throw new ArgumentNullException(nameof(action)); | ||
} | ||
|
||
var timer = new DispatcherTimer(); | ||
|
||
var state1 = state; | ||
|
||
timer.Tick += (o, 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 = _ => _; | ||
} | ||
}); | ||
} | ||
} | ||
} |
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,42 @@ | ||
// Copyright (c) 2019 .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.Reactive.Concurrency; | ||
using System.Reactive.PlatformServices; | ||
|
||
namespace ReactiveUI | ||
{ | ||
/// <summary> | ||
/// UWP platform registrations. | ||
/// </summary> | ||
/// <seealso cref="ReactiveUI.IWantsToRegisterStuff" /> | ||
public class PlatformRegistrations : IWantsToRegisterStuff | ||
{ | ||
/// <inheritdoc/> | ||
public void Register(Action<Func<object>, Type> registerFunction) | ||
{ | ||
registerFunction(() => new PlatformOperations(), typeof(IPlatformOperations)); | ||
registerFunction(() => new ActivationForViewFetcher(), typeof(IActivationForViewFetcher)); | ||
registerFunction(() => new DependencyObjectObservableForProperty(), typeof(ICreatesObservableForProperty)); | ||
registerFunction(() => new BooleanToVisibilityTypeConverter(), typeof(IBindingTypeConverter)); | ||
registerFunction(() => new AutoDataTemplateBindingHook(), typeof(IPropertyBindingHook)); | ||
registerFunction(() => new WinRTAppDataDriver(), typeof(ISuspensionDriver)); | ||
|
||
#if NETSTANDARD | ||
if (WasmPlatformEnlightenmentProvider.IsWasm) | ||
{ | ||
RxApp.TaskpoolScheduler = WasmScheduler.Default; | ||
RxApp.MainThreadScheduler = WasmScheduler.Default; | ||
} | ||
else | ||
#endif | ||
{ | ||
RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; | ||
RxApp.MainThreadScheduler = new WaitForDispatcherScheduler(() => CoreDispatcherScheduler.Current); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.