Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Added Uno support #2067

Merged
merged 35 commits into from
Jun 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
0c38232
Added wasm files.
weitzhandler Jun 12, 2019
bee2b58
Fixed ReactivePage.OnViewModelChanging implementation
weitzhandler Jun 12, 2019
08a8c04
Merge branch 'master' into master
glennawatson Jun 12, 2019
7bbab71
Merge branch 'master' into master
glennawatson Jun 15, 2019
ea5fd6c
Merge branch 'master' into master
glennawatson Jun 16, 2019
009d692
Added Uno platform initial support
weitzhandler Jun 17, 2019
21ae794
Fixed linked files references
weitzhandler Jun 17, 2019
5f788b6
Merge branch 'master' into master
weitzhandler Jun 17, 2019
99e1f39
Fix wrong preprocessor directive
weitzhandler Jun 17, 2019
10ee94a
Merge branch 'master' of https://github.com/weitzhandler/ReactiveUI
weitzhandler Jun 17, 2019
c284a5e
Fixes
glennawatson Jun 17, 2019
fc9e3bc
Updated message suppression
weitzhandler Jun 17, 2019
3472b76
Expanded suppression messages
weitzhandler Jun 17, 2019
780abf2
Deleting g.cs file manually
weitzhandler Jun 17, 2019
684c979
extensions for reactiveui
ghuntley Jun 18, 2019
ba00181
Remove HAS_UNO directive from production
weitzhandler Jun 18, 2019
7497685
Updated Uno Platform brand name
weitzhandler Jun 18, 2019
36bdda5
Updated Reactive.Wasm to the new NuGet package
weitzhandler Jun 18, 2019
bf55094
further work
glennawatson Jun 18, 2019
d20cb8e
merge
glennawatson Jun 18, 2019
0e3932c
Add back in the ReactivePage.cs
glennawatson Jun 18, 2019
14ea219
remove the reactive page since that will be added to the windows-comm…
glennawatson Jun 18, 2019
1bcfb19
Merge branch 'master' into master
glennawatson Jun 18, 2019
335034d
Add Uno to RxUI initialization
weitzhandler Jun 19, 2019
9f064fb
Added some README stuff about Uno
weitzhandler Jun 19, 2019
a1db0c2
Update README.md
weitzhandler Jun 19, 2019
0973481
Merge branch 'master' into master
glennawatson Jun 19, 2019
fe9c1c7
Reverted README changes
weitzhandler Jun 19, 2019
d774634
Merge branch 'master' into master
glennawatson Jun 23, 2019
8b637b0
Merge branch 'master' into master
glennawatson Jun 25, 2019
aaf53e7
adding results to parent directories
glennawatson Jun 25, 2019
f6148b7
Further work on getting uno in
glennawatson Jun 26, 2019
214b5a8
Remove the events project, will be back in another iteration
glennawatson Jun 26, 2019
a25e37f
turn off exception when in WASM mode
glennawatson Jun 26, 2019
0df2271
grouped registrations
RLittlesII Jun 26, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var packageWhitelist = new List<FilePath>
MakeAbsolute(File("./src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj")),
MakeAbsolute(File("./src/ReactiveUI.AndroidSupport/ReactiveUI.AndroidSupport.csproj")),
MakeAbsolute(File("./src/ReactiveUI.XamForms/ReactiveUI.XamForms.csproj")),
MakeAbsolute(File("./src/ReactiveUI.Uno/ReactiveUI.Uno.csproj")),
};

if (IsRunningOnWindows())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.AndroidSupport")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Tests")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Uno")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Winforms")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Wpf")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.XamForms")]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.AndroidSupport")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Tests")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Uno")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Winforms")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Wpf")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.XamForms")]
Expand Down
66 changes: 66 additions & 0 deletions src/ReactiveUI.Uno/ActivationForViewFetcher.cs
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();
}
}
}
251 changes: 251 additions & 0 deletions src/ReactiveUI.Uno/CoreDispatcherScheduler.cs
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 = _ => _;
}
});
}
}
}
42 changes: 42 additions & 0 deletions src/ReactiveUI.Uno/PlatformRegistrations.cs
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);
}
}
}
}
Loading