DeclarativeProperty nullability fixes
This commit is contained in:
@@ -88,7 +88,7 @@ public record SizeScanContainer : ISizeScanContainer
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public required IDeclarativeProperty<string> Status { get; init; } = new DeclarativeProperty<string>();
|
||||
public required IDeclarativeProperty<string> Status { get; init; } = new DeclarativeProperty<string>(string.Empty);
|
||||
|
||||
public required SizeScanTask SizeScanTask { get; init; }
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ public class SizeScanTask : ISizeScanTask
|
||||
private readonly ILogger<SizeScanTask> _logger;
|
||||
private Thread? _sizeScanThread;
|
||||
private static int _searchId = 1;
|
||||
private readonly DeclarativeProperty<string> _containerStatus = new();
|
||||
private readonly DeclarativeProperty<string> _containerStatus = new(string.Empty);
|
||||
private readonly IDeclarativeProperty<string> _containerStatusDebounced;
|
||||
public ISizeScanContainer SizeSizeScanContainer { get; private set; } = null!;
|
||||
public bool IsRunning { get; private set; }
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace FileTime.App.Core.ViewModels;
|
||||
public abstract partial class AppStateBase : IAppState
|
||||
{
|
||||
private readonly BehaviorSubject<string?> _searchText = new(null);
|
||||
private readonly DeclarativeProperty<ITabViewModel?> _selectedTab = new();
|
||||
private readonly DeclarativeProperty<ITabViewModel?> _selectedTab = new(null);
|
||||
private readonly DeclarativeProperty<ViewMode> _viewMode = new(Models.Enums.ViewMode.Default);
|
||||
private readonly ObservableCollection<ITabViewModel> _tabs = new();
|
||||
private readonly DeclarativeProperty<string?> _rapidTravelText;
|
||||
|
||||
@@ -15,11 +15,11 @@ public class CopyCommand : CommandBase, ITransportationCommand
|
||||
private readonly CancellationTokenSource _cancellationTokenSource = new();
|
||||
|
||||
private readonly List<OperationProgress> _operationProgresses = new();
|
||||
private readonly DeclarativeProperty<OperationProgress?> _currentOperationProgress = new();
|
||||
private readonly DeclarativeProperty<OperationProgress?> _currentOperationProgress = new(null);
|
||||
|
||||
private long _recentTotalSum;
|
||||
private readonly DeclarativeProperty<long> _recentTotalProcessed = new();
|
||||
private readonly DeclarativeProperty<DateTime> _recentStartTime = new();
|
||||
private readonly DeclarativeProperty<long> _recentTotalProcessed = new(0);
|
||||
private readonly DeclarativeProperty<DateTime> _recentStartTime = new(DateTime.Now);
|
||||
|
||||
public IReadOnlyList<FullName> Sources { get; }
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@ public class Tab : ITab
|
||||
|
||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||
private readonly ITabEvents _tabEvents;
|
||||
private readonly DeclarativeProperty<IContainer?> _currentLocation = new();
|
||||
private readonly DeclarativeProperty<IContainer?> _currentLocationForced = new();
|
||||
private readonly DeclarativeProperty<AbsolutePath?> _currentRequestItem = new();
|
||||
private readonly DeclarativeProperty<IContainer?> _currentLocation = new(null);
|
||||
private readonly DeclarativeProperty<IContainer?> _currentLocationForced = new(null);
|
||||
private readonly DeclarativeProperty<AbsolutePath?> _currentRequestItem = new(null);
|
||||
private readonly ObservableCollection<ItemFilter> _itemFilters = new();
|
||||
private readonly CircularBuffer<FullName> _history = new(20);
|
||||
private readonly CircularBuffer<FullName> _future = new(20);
|
||||
@@ -64,28 +64,26 @@ public class Tab : ITab
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
CurrentItems = DeclarativePropertyHelpers.CombineLatest(
|
||||
IDeclarativeProperty<ObservableCollection<IItem>?> asd1 = DeclarativePropertyHelpers.CombineLatest(
|
||||
CurrentLocation,
|
||||
itemFiltersProperty,
|
||||
(container, filters) =>
|
||||
Task<ObservableCollection<IItem>?> (container, filters) =>
|
||||
{
|
||||
ObservableCollection<IItem>? items = null;
|
||||
|
||||
if (container is not null)
|
||||
{
|
||||
if (container is null) return Task.FromResult(items);
|
||||
|
||||
items = container
|
||||
.Items
|
||||
.Selecting<AbsolutePath, IItem>(i => MapItem(i));
|
||||
|
||||
if (filters is not null)
|
||||
{
|
||||
items = items.Filtering(i => filters.All(f => f.Filter(i)));
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult(items);
|
||||
return Task.FromResult(items)!;
|
||||
}
|
||||
).CombineLatest(
|
||||
);
|
||||
|
||||
var asd2 = asd1.CombineLatest(
|
||||
Ordering.Map(ordering =>
|
||||
{
|
||||
var (itemComparer, order) = ordering switch
|
||||
@@ -103,7 +101,7 @@ public class Tab : ITab
|
||||
|
||||
return (itemComparer, order, ordering is not ItemOrdering.Name and not ItemOrdering.NameDesc);
|
||||
}),
|
||||
(items, ordering) =>
|
||||
Task<ObservableCollection<IItem>?> (items, ordering) =>
|
||||
{
|
||||
if (items is null) return Task.FromResult<ObservableCollection<IItem>?>(null);
|
||||
|
||||
@@ -117,14 +115,16 @@ public class Tab : ITab
|
||||
? primaryOrderedItems.ThenOrdering(i => i.DisplayName)
|
||||
: primaryOrderedItems;
|
||||
|
||||
return Task.FromResult(orderedItems);
|
||||
return Task.FromResult<ObservableCollection<IItem>?>(orderedItems);
|
||||
}
|
||||
);
|
||||
|
||||
CurrentItems = asd2;
|
||||
|
||||
CurrentSelectedItem = DeclarativePropertyHelpers.CombineLatest(
|
||||
CurrentItems.Watch<ObservableCollection<IItem>, IItem>(),
|
||||
CurrentItems.Watch<ObservableCollection<IItem>?, IItem>(),
|
||||
_currentRequestItem.DistinctUntilChanged(),
|
||||
(items, selected) =>
|
||||
Task<AbsolutePath?> (items, selected) =>
|
||||
{
|
||||
var itemSelectingContext = new LastItemSelectingContext(CurrentLocation.Value);
|
||||
var lastItemSelectingContext = _lastItemSelectingContext;
|
||||
@@ -143,7 +143,7 @@ public class Tab : ITab
|
||||
var candidate = _selectedItemCandidates.FirstOrDefault(c => items.Any(i => i.FullName?.Path == c.Path.Path));
|
||||
if (candidate != null)
|
||||
{
|
||||
return Task.FromResult(candidate);
|
||||
return Task.FromResult<AbsolutePath?>(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ public class Tab : ITab
|
||||
DeclarativePropertyHelpers.CombineLatest(
|
||||
CurrentItems,
|
||||
CurrentSelectedItem,
|
||||
(items, selected) =>
|
||||
Task<IEnumerable<AbsolutePath>?> (items, selected) =>
|
||||
{
|
||||
if (items is null || selected is null) return Task.FromResult<IEnumerable<AbsolutePath>?>(null);
|
||||
var primaryCandidates = items.SkipWhile(i => i.FullName is {Path: var p} && p != selected.Path.Path).Skip(1);
|
||||
@@ -169,7 +169,7 @@ public class Tab : ITab
|
||||
.Concat(secondaryCandidates)
|
||||
.Select(c => new AbsolutePath(_timelessContentProvider, c));
|
||||
|
||||
return Task.FromResult(candidates);
|
||||
return Task.FromResult<IEnumerable<AbsolutePath>?>(candidates);
|
||||
})
|
||||
.Subscribe(candidates =>
|
||||
{
|
||||
|
||||
@@ -8,6 +8,6 @@ public interface IMainWindowViewModelBase
|
||||
DeclarativeProperty<string> Title { get; }
|
||||
bool Loading { get; }
|
||||
IObservable<string?> MainFont { get; }
|
||||
DeclarativeProperty<string> FatalError { get; }
|
||||
DeclarativeProperty<string?> FatalError { get; }
|
||||
IReadOnlyList<WindowTransparencyLevel> TransparencyLevelHint { get; }
|
||||
}
|
||||
@@ -9,6 +9,6 @@ public class MainWindowLoadingViewModel : IMainWindowViewModelBase
|
||||
public bool Loading => true;
|
||||
public IObservable<string?> MainFont { get; } = new BehaviorSubject<string?>("");
|
||||
public DeclarativeProperty<string> Title { get; } = new("Loading...");
|
||||
public DeclarativeProperty<string> FatalError { get; } = new();
|
||||
public DeclarativeProperty<string?> FatalError { get; } = new(null);
|
||||
public IReadOnlyList<WindowTransparencyLevel> TransparencyLevelHint { get; } = new[] {WindowTransparencyLevel.AcrylicBlur};
|
||||
}
|
||||
@@ -53,10 +53,10 @@ public partial class MainWindowViewModel : IMainWindowViewModel
|
||||
private readonly OcConsumer _rootDriveInfosConsumer = new();
|
||||
public bool Loading => false;
|
||||
public IObservable<string?> MainFont => _fontService.MainFont.Select(x => x ?? "");
|
||||
public DeclarativeProperty<string> FatalError { get; } = new();
|
||||
public DeclarativeProperty<string?> FatalError { get; } = new(null);
|
||||
public IReadOnlyList<WindowTransparencyLevel> TransparencyLevelHint { get; } = new[] {WindowTransparencyLevel.Blur};
|
||||
public IGuiAppState AppState => _appState;
|
||||
public DeclarativeProperty<string> Title { get; } = new();
|
||||
public DeclarativeProperty<string> Title { get; } = new(string.Empty);
|
||||
public Thickness IconStatusPanelMargin { get; private set; } = new(20, 10, 10, 10);
|
||||
public Action? FocusDefaultElement { get; set; }
|
||||
public Action? ShowWindow { get; set; }
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
namespace DeclarativeProperty;
|
||||
|
||||
public class CollectionRepeaterProperty<TCollection, TItem> : DeclarativePropertyBase<TCollection>
|
||||
where TCollection : IList<TItem>, INotifyCollectionChanged
|
||||
where TCollection : IList<TItem>?, INotifyCollectionChanged?
|
||||
{
|
||||
private TCollection? _currentCollection;
|
||||
|
||||
public CollectionRepeaterProperty(IDeclarativeProperty<TCollection?> from) : base(from.Value)
|
||||
public CollectionRepeaterProperty(IDeclarativeProperty<TCollection> from) : base(from.Value)
|
||||
{
|
||||
_currentCollection = from.Value;
|
||||
if (from.Value is { } value)
|
||||
@@ -18,7 +18,7 @@ public class CollectionRepeaterProperty<TCollection, TItem> : DeclarativePropert
|
||||
AddDisposable(from.Subscribe(Handle));
|
||||
}
|
||||
|
||||
public CollectionRepeaterProperty(TCollection? collection) : base(collection)
|
||||
public CollectionRepeaterProperty(TCollection collection) : base(collection)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(collection);
|
||||
|
||||
@@ -32,7 +32,7 @@ public class CollectionRepeaterProperty<TCollection, TItem> : DeclarativePropert
|
||||
t.Wait();
|
||||
}
|
||||
|
||||
private async Task Handle(TCollection? collection, CancellationToken cancellationToken = default)
|
||||
private async Task Handle(TCollection collection, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_currentCollection is { } currentCollection)
|
||||
{
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
public class CombineAllProperty<T, TResult> : DeclarativePropertyBase<TResult>
|
||||
{
|
||||
private readonly List<IDeclarativeProperty<T>> _sources;
|
||||
private readonly Func<IEnumerable<T>, Task<TResult?>> _combiner;
|
||||
private readonly Func<IEnumerable<T>, Task<TResult>> _combiner;
|
||||
|
||||
public CombineAllProperty(
|
||||
IEnumerable<IDeclarativeProperty<T>> sources,
|
||||
Func<IEnumerable<T>, Task<TResult?>> combiner,
|
||||
Action<TResult?>? setValueHook = null) : base(setValueHook)
|
||||
Func<IEnumerable<T>, Task<TResult>> combiner,
|
||||
Action<TResult>? setValueHook = null) : base(default!, setValueHook)
|
||||
{
|
||||
var sourcesList = sources.ToList();
|
||||
_sources = sourcesList;
|
||||
_combiner = combiner;
|
||||
|
||||
var initialValueTask = _combiner(sourcesList.Select(p => p.Value)!);
|
||||
var initialValueTask = Task.Run(async () => await _combiner(sourcesList.Select(p => p.Value)));
|
||||
initialValueTask.Wait();
|
||||
SetNewValueSync(initialValueTask.Result);
|
||||
|
||||
@@ -24,12 +24,12 @@ public class CombineAllProperty<T, TResult> : DeclarativePropertyBase<TResult>
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnSourceChanged(T? arg1, CancellationToken arg2) => await Update();
|
||||
private async Task OnSourceChanged(T arg1, CancellationToken arg2) => await Update();
|
||||
|
||||
private async Task Update()
|
||||
{
|
||||
var values = _sources.Select(p => p.Value);
|
||||
var result = await _combiner(values!);
|
||||
var result = await _combiner(values);
|
||||
|
||||
await SetNewValueAsync(result);
|
||||
}
|
||||
|
||||
@@ -2,23 +2,27 @@
|
||||
|
||||
public sealed class CombineLatestProperty<T1, T2, TResult> : DeclarativePropertyBase<TResult>
|
||||
{
|
||||
private readonly Func<T1?, T2?, Task<TResult>> _func;
|
||||
private T1? _value1;
|
||||
private T2? _value2;
|
||||
private readonly Func<T1, T2, Task<TResult>> _func;
|
||||
private T1 _value1;
|
||||
private T2 _value2;
|
||||
|
||||
public CombineLatestProperty(
|
||||
IDeclarativeProperty<T1> prop1,
|
||||
IDeclarativeProperty<T2> prop2,
|
||||
Func<T1?, T2?, Task<TResult>> func,
|
||||
Action<TResult?>? setValueHook = null) : base(setValueHook)
|
||||
Func<T1, T2, Task<TResult>> func,
|
||||
Action<TResult>? setValueHook = null) : base(default!, setValueHook)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(prop1);
|
||||
ArgumentNullException.ThrowIfNull(prop2);
|
||||
|
||||
_func = func;
|
||||
|
||||
_value1 = prop1.Value is null ? default : prop1.Value;
|
||||
_value2 = prop2.Value is null ? default : prop2.Value;
|
||||
_value1 = prop1.Value;
|
||||
_value2 = prop2.Value;
|
||||
|
||||
var initialValueTask = Task.Run(async () => await _func(_value1, _value2));
|
||||
initialValueTask.Wait();
|
||||
SetNewValueSync(initialValueTask.Result);
|
||||
|
||||
prop1.Subscribe(async (value1, token) =>
|
||||
{
|
||||
@@ -51,7 +55,7 @@ public sealed class CombineLatestProperty<T1, T2, T3, TResult> : DeclarativeProp
|
||||
IDeclarativeProperty<T2> prop2,
|
||||
IDeclarativeProperty<T3> prop3,
|
||||
Func<T1?, T2?, T3?,Task<TResult>> func,
|
||||
Action<TResult?>? setValueHook = null) : base(setValueHook)
|
||||
Action<TResult>? setValueHook = null) : base(default!, setValueHook)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(prop1);
|
||||
ArgumentNullException.ThrowIfNull(prop2);
|
||||
@@ -59,9 +63,13 @@ public sealed class CombineLatestProperty<T1, T2, T3, TResult> : DeclarativeProp
|
||||
|
||||
_func = func;
|
||||
|
||||
_value1 = prop1.Value is null ? default : prop1.Value;
|
||||
_value2 = prop2.Value is null ? default : prop2.Value;
|
||||
_value3 = prop3.Value is null ? default : prop3.Value;
|
||||
_value1 = prop1.Value;
|
||||
_value2 = prop2.Value;
|
||||
_value3 = prop3.Value;
|
||||
|
||||
var initialValueTask = Task.Run(async () => await _func(_value1, _value2, _value3));
|
||||
initialValueTask.Wait();
|
||||
SetNewValueSync(initialValueTask.Result);
|
||||
|
||||
prop1.Subscribe(async (value1, token) =>
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ public sealed class CombineProperty<TFrom, TTo> : DeclarativePropertyBase<TTo>
|
||||
private readonly Func<IReadOnlyList<TFrom?>, Task<TTo>> _combiner;
|
||||
private readonly List<IDeclarativeProperty<TFrom>> _sourceProperties = new();
|
||||
|
||||
public CombineProperty(Func<IReadOnlyList<TFrom?>, Task<TTo>> combiner)
|
||||
public CombineProperty(Func<IReadOnlyList<TFrom?>, Task<TTo>> combiner) : base(default!)
|
||||
{
|
||||
_combiner = combiner;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
public sealed class DebounceProperty<T> : DeclarativePropertyBase<T>
|
||||
{
|
||||
private readonly object _lock = new();
|
||||
private readonly Func<T?, TimeSpan> _interval;
|
||||
private readonly Func<T, TimeSpan> _interval;
|
||||
private DateTime _startTime = DateTime.MinValue;
|
||||
private T? _nextValue;
|
||||
private T _nextValue = default!;
|
||||
private CancellationToken _nextCancellationToken;
|
||||
private bool _isThrottleTaskRunning;
|
||||
public bool ResetTimer { get; init; }
|
||||
@@ -13,14 +13,14 @@ public sealed class DebounceProperty<T> : DeclarativePropertyBase<T>
|
||||
|
||||
public DebounceProperty(
|
||||
IDeclarativeProperty<T> from,
|
||||
Func<T?, TimeSpan> interval,
|
||||
Action<T?>? setValueHook = null) : base(from.Value, setValueHook)
|
||||
Func<T, TimeSpan> interval,
|
||||
Action<T>? setValueHook = null) : base(from.Value, setValueHook)
|
||||
{
|
||||
_interval = interval;
|
||||
AddDisposable(from.Subscribe(SetValue));
|
||||
}
|
||||
|
||||
private Task SetValue(T? next, CancellationToken cancellationToken = default)
|
||||
private Task SetValue(T next, CancellationToken cancellationToken = default)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
@@ -38,7 +38,7 @@ public sealed class DebounceProperty<T> : DeclarativePropertyBase<T>
|
||||
|
||||
_startTime = DateTime.Now;
|
||||
_isThrottleTaskRunning = true;
|
||||
Task.Run(async () => await StartDebounceTask());
|
||||
Task.Run(StartDebounceTask);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
@@ -51,7 +51,7 @@ public sealed class DebounceProperty<T> : DeclarativePropertyBase<T>
|
||||
await Task.Delay(WaitInterval);
|
||||
}
|
||||
|
||||
T? next;
|
||||
T next;
|
||||
CancellationToken cancellationToken;
|
||||
lock (_lock)
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ public static class DeclarativePropertyHelpers
|
||||
IDeclarativeProperty<T1> prop1,
|
||||
IDeclarativeProperty<T2> prop2,
|
||||
Func<T1, T2, Task<TResult>> func,
|
||||
Action<TResult?>? setValueHook = null)
|
||||
=> new(prop1, prop2, func!, setValueHook);
|
||||
Action<TResult>? setValueHook = null)
|
||||
=> new(prop1, prop2, func, setValueHook);
|
||||
|
||||
public static CombineLatestProperty<T1, T2, T3, TResult> CombineLatest<T1, T2, T3, TResult>(
|
||||
IDeclarativeProperty<T1> prop1,
|
||||
IDeclarativeProperty<T2> prop2,
|
||||
IDeclarativeProperty<T3> prop3,
|
||||
Func<T1, T2, T3, Task<TResult>> func,
|
||||
Action<TResult?>? setValueHook = null)
|
||||
Action<TResult>? setValueHook = null)
|
||||
=> new(prop1, prop2, prop3, func!, setValueHook);
|
||||
|
||||
public static MergeProperty<T> Merge<T>(params IDeclarativeProperty<T>[] props)
|
||||
@@ -23,11 +23,7 @@ public static class DeclarativePropertyHelpers
|
||||
|
||||
public sealed class DeclarativeProperty<T> : DeclarativePropertyBase<T>
|
||||
{
|
||||
public DeclarativeProperty(Action<T?>? setValueHook = null) : base(setValueHook)
|
||||
{
|
||||
}
|
||||
|
||||
public DeclarativeProperty(T initialValue, Action<T?>? setValueHook = null) : base(initialValue, setValueHook)
|
||||
public DeclarativeProperty(T initialValue, Action<T>? setValueHook = null) : base(initialValue, setValueHook)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ namespace DeclarativeProperty;
|
||||
|
||||
public abstract class DeclarativePropertyBase<T> : IDeclarativeProperty<T>
|
||||
{
|
||||
private readonly List<Func<T?, CancellationToken, Task>> _subscribers = new();
|
||||
private readonly Action<T?>? _setValueHook;
|
||||
private readonly List<Func<T, CancellationToken, Task>> _subscribers = new();
|
||||
private readonly Action<T>? _setValueHook;
|
||||
private readonly List<IDisposable> _disposables = new();
|
||||
private readonly List<Func<IDeclarativeProperty<T>, T, IDisposable?>> _subscribeTriggers = new();
|
||||
private readonly List<Action<IDeclarativeProperty<T>, T>> _unsubscribeTriggers = new();
|
||||
@@ -14,29 +14,24 @@ public abstract class DeclarativePropertyBase<T> : IDeclarativeProperty<T>
|
||||
private readonly object _triggerLock = new();
|
||||
private readonly object _subscriberLock = new();
|
||||
|
||||
private T? _value;
|
||||
private T _value;
|
||||
|
||||
public T? Value
|
||||
public T Value
|
||||
{
|
||||
get => _value;
|
||||
set => _setValueHook?.Invoke(value);
|
||||
}
|
||||
|
||||
protected DeclarativePropertyBase(Action<T?>? setValueHook = null)
|
||||
{
|
||||
_setValueHook = setValueHook;
|
||||
}
|
||||
|
||||
protected DeclarativePropertyBase(T? initialValue, Action<T?>? setValueHook = null)
|
||||
protected DeclarativePropertyBase(T initialValue, Action<T>? setValueHook = null)
|
||||
{
|
||||
_setValueHook = setValueHook;
|
||||
_value = initialValue;
|
||||
}
|
||||
|
||||
|
||||
protected async Task NotifySubscribersAsync(T? newValue, CancellationToken cancellationToken = default)
|
||||
protected async Task NotifySubscribersAsync(T newValue, CancellationToken cancellationToken = default)
|
||||
{
|
||||
List<Func<T?, CancellationToken, Task>> subscribers;
|
||||
List<Func<T, CancellationToken, Task>> subscribers;
|
||||
lock (_subscriberLock)
|
||||
{
|
||||
subscribers = _subscribers.ToList();
|
||||
@@ -54,7 +49,7 @@ public abstract class DeclarativePropertyBase<T> : IDeclarativeProperty<T>
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
public IDisposable Subscribe(Func<T?, CancellationToken, Task> onChange)
|
||||
public IDisposable Subscribe(Func<T, CancellationToken, Task> onChange)
|
||||
{
|
||||
lock (_subscriberLock)
|
||||
{
|
||||
@@ -70,7 +65,7 @@ public abstract class DeclarativePropertyBase<T> : IDeclarativeProperty<T>
|
||||
{
|
||||
lock (_subscriberLock)
|
||||
{
|
||||
_subscribers.Remove(onChange!);
|
||||
_subscribers.Remove(onChange);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,14 +94,14 @@ public abstract class DeclarativePropertyBase<T> : IDeclarativeProperty<T>
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
protected async Task SetNewValueAsync(T? newValue, CancellationToken cancellationToken = default)
|
||||
protected async Task SetNewValueAsync(T newValue, CancellationToken cancellationToken = default)
|
||||
{
|
||||
SetNewValueSync(newValue, cancellationToken);
|
||||
if (cancellationToken.IsCancellationRequested) return;
|
||||
await NotifySubscribersAsync(newValue, cancellationToken);
|
||||
}
|
||||
|
||||
protected void SetNewValueSync(T? newValue, CancellationToken cancellationToken = default)
|
||||
protected void SetNewValueSync(T newValue, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!(Value?.Equals(newValue) ?? false))
|
||||
{
|
||||
|
||||
@@ -21,16 +21,16 @@ public static class DeclarativePropertyExtensions
|
||||
public static IDeclarativeProperty<T> DistinctUntilChanged<T>(this IDeclarativeProperty<T> from)
|
||||
=> new DistinctUntilChangedProperty<T>(from);
|
||||
|
||||
public static IDeclarativeProperty<TTo> Map<TFrom, TTo>(this IDeclarativeProperty<TFrom?> from, Func<TFrom?, Task<TTo?>> mapper)
|
||||
public static IDeclarativeProperty<TTo> Map<TFrom, TTo>(this IDeclarativeProperty<TFrom> from, Func<TFrom, Task<TTo>> mapper)
|
||||
=> Map(from, async (v, _) => await mapper(v));
|
||||
|
||||
public static IDeclarativeProperty<TTo> Map<TFrom, TTo>(this IDeclarativeProperty<TFrom?> from, Func<TFrom?, CancellationToken, Task<TTo?>> mapper)
|
||||
=> new MapProperty<TFrom?, TTo>(mapper, from);
|
||||
public static IDeclarativeProperty<TTo> Map<TFrom, TTo>(this IDeclarativeProperty<TFrom> from, Func<TFrom, CancellationToken, Task<TTo>> mapper)
|
||||
=> new MapProperty<TFrom, TTo>(mapper, from);
|
||||
|
||||
public static IDeclarativeProperty<TTo?> Map<TFrom, TTo>(this IDeclarativeProperty<TFrom?> from, Func<TFrom?, TTo?> mapper)
|
||||
=> new MapProperty<TFrom?, TTo?>((next, _) => Task.FromResult(mapper(next)), from);
|
||||
public static IDeclarativeProperty<TTo> Map<TFrom, TTo>(this IDeclarativeProperty<TFrom> from, Func<TFrom, TTo> mapper)
|
||||
=> new MapProperty<TFrom, TTo>((next, _) => Task.FromResult(mapper(next)), from);
|
||||
|
||||
public static async Task<IDeclarativeProperty<TTo>> MapAsync<TFrom, TTo>(this IDeclarativeProperty<TFrom?> from, Func<TFrom?, CancellationToken, Task<TTo?>> mapper)
|
||||
public static async Task<IDeclarativeProperty<TTo>> MapAsync<TFrom, TTo>(this IDeclarativeProperty<TFrom> from, Func<TFrom, CancellationToken, Task<TTo>> mapper)
|
||||
=> await MapProperty<TFrom, TTo>.CreateAsync(mapper, from);
|
||||
|
||||
public static async Task<IDeclarativeProperty<TTo?>> MapAsync<TFrom, TTo>(this IDeclarativeProperty<TFrom?> from, Func<TFrom?, TTo?> mapper)
|
||||
@@ -43,39 +43,39 @@ public static class DeclarativePropertyExtensions
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
public static IDisposable Subscribe<T>(this IDeclarativeProperty<T> property, Action<T?> onChange)
|
||||
public static IDisposable Subscribe<T>(this IDeclarativeProperty<T> property, Action<T> onChange)
|
||||
=> property.Subscribe((value, _) =>
|
||||
{
|
||||
onChange(value);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
public static IDeclarativeProperty<T> Extract<T>(
|
||||
this IDeclarativeProperty<ReadOnlyObservableCollection<T>> from,
|
||||
Func<IList<T>?, T?> extractor
|
||||
public static IDeclarativeProperty<TResult> Extract<T, TResult>(
|
||||
this IDeclarativeProperty<ReadOnlyObservableCollection<T>?> from,
|
||||
Func<IList<T>?, TResult> extractor
|
||||
)
|
||||
=> new ExtractorProperty<T>(from, extractor);
|
||||
=> new ExtractorProperty<T, TResult>(from, extractor);
|
||||
|
||||
public static IDeclarativeProperty<T> Extract<T>(
|
||||
this IDeclarativeProperty<ObservableCollection<T>> from,
|
||||
Func<IList<T>?, T?> extractor
|
||||
public static IDeclarativeProperty<TResult> Extract<T, TResult>(
|
||||
this IDeclarativeProperty<ObservableCollection<T>?> from,
|
||||
Func<IList<T>?, TResult> extractor
|
||||
)
|
||||
=> new ExtractorProperty<T>(from, extractor);
|
||||
=> new ExtractorProperty<T, TResult>(from, extractor);
|
||||
|
||||
public static IDeclarativeProperty<T> Extract<T>(
|
||||
this IDeclarativeProperty<CollectionComputing<T>> from,
|
||||
Func<IList<T>?, T?> extractor
|
||||
public static IDeclarativeProperty<TResult> Extract<T, TResult>(
|
||||
this IDeclarativeProperty<CollectionComputing<T>?> from,
|
||||
Func<IList<T>?, TResult> extractor
|
||||
)
|
||||
=> new ExtractorProperty<T>(from, extractor);
|
||||
=> new ExtractorProperty<T, TResult>(from, extractor);
|
||||
|
||||
public static IDeclarativeProperty<TCollection> Watch<TCollection, TItem>(
|
||||
this IDeclarativeProperty<TCollection?> collection)
|
||||
where TCollection : IList<TItem>, INotifyCollectionChanged
|
||||
this IDeclarativeProperty<TCollection> collection)
|
||||
where TCollection : IList<TItem>?, INotifyCollectionChanged?
|
||||
=> new CollectionRepeaterProperty<TCollection, TItem>(collection);
|
||||
|
||||
public static IDeclarativeProperty<TCollection> Watch<TCollection, TItem>(
|
||||
this TCollection collection)
|
||||
where TCollection : IList<TItem>, INotifyCollectionChanged
|
||||
where TCollection : IList<TItem>?, INotifyCollectionChanged?
|
||||
=> new CollectionRepeaterProperty<TCollection, TItem>(collection);
|
||||
|
||||
public static IDeclarativeProperty<ObservableCollection<TItem>> Watch<TItem>(
|
||||
@@ -90,16 +90,16 @@ public static class DeclarativePropertyExtensions
|
||||
public static IDeclarativeProperty<TResult> CombineLatest<T1, T2, TResult>(
|
||||
this IDeclarativeProperty<T1> prop1,
|
||||
IDeclarativeProperty<T2> prop2,
|
||||
Func<T1, T2, Task<TResult?>> func,
|
||||
Func<T1, T2, Task<TResult>> func,
|
||||
Action<TResult?>? setValueHook = null)
|
||||
=> new CombineLatestProperty<T1,T2,TResult>(prop1, prop2, func!, setValueHook);
|
||||
=> new CombineLatestProperty<T1,T2,TResult>(prop1, prop2, func, setValueHook);
|
||||
|
||||
public static IDeclarativeProperty<T?> Switch<T>(this IDeclarativeProperty<IDeclarativeProperty<T?>?> from)
|
||||
=> new SwitchProperty<T?>(from);
|
||||
public static IDeclarativeProperty<T> Switch<T>(this IDeclarativeProperty<IDeclarativeProperty<T>?> from)
|
||||
=> new SwitchProperty<T>(from);
|
||||
|
||||
public static IDeclarativeProperty<TResult> CombineAll<T, TResult>(
|
||||
this IEnumerable<IDeclarativeProperty<T>> sources,
|
||||
Func<IEnumerable<T>, Task<TResult?>> combiner,
|
||||
Func<IEnumerable<T>, Task<TResult>> combiner,
|
||||
Action<TResult?>? setValueHook = null)
|
||||
=> new CombineAllProperty<T,TResult>(sources, combiner, setValueHook);
|
||||
}
|
||||
@@ -11,7 +11,7 @@ public sealed class DistinctUntilChangedProperty<T> : DeclarativePropertyBase<T>
|
||||
AddDisposable(from.Subscribe(Handle));
|
||||
}
|
||||
|
||||
async Task Handle(T? next, CancellationToken cancellationToken = default)
|
||||
async Task Handle(T next, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_comparer is { } comparer)
|
||||
{
|
||||
@@ -20,8 +20,10 @@ public sealed class DistinctUntilChangedProperty<T> : DeclarativePropertyBase<T>
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ((next is null && Value is null && !_firstFire)
|
||||
|| (Value?.Equals(next) ?? false))
|
||||
else if (
|
||||
(next is null && Value is null && !_firstFire)
|
||||
|| (Value?.Equals(next) ?? false)
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ using ObservableComputations;
|
||||
|
||||
namespace DeclarativeProperty;
|
||||
|
||||
public sealed class ExtractorProperty<T> : DeclarativePropertyBase<T>
|
||||
public sealed class ExtractorProperty<T, TResult> : DeclarativePropertyBase<TResult>
|
||||
{
|
||||
private interface ICollectionWrapper : IDisposable
|
||||
{
|
||||
@@ -108,12 +108,12 @@ public sealed class ExtractorProperty<T> : DeclarativePropertyBase<T>
|
||||
) => new CollectionComputingWrapper(collection, handler);
|
||||
}
|
||||
|
||||
private readonly Func<IList<T>?, T?> _extractor;
|
||||
private readonly Func<IList<T>?, TResult> _extractor;
|
||||
private ICollectionWrapper? _collectionWrapper;
|
||||
|
||||
public ExtractorProperty(
|
||||
IDeclarativeProperty<ObservableCollection<T>> from,
|
||||
Func<IList<T>?, T?> extractor) : base(extractor(from.Value))
|
||||
IDeclarativeProperty<ObservableCollection<T>?> from,
|
||||
Func<IList<T>?, TResult> extractor) : base(extractor(from.Value))
|
||||
{
|
||||
_extractor = extractor;
|
||||
_collectionWrapper = from.Value is null
|
||||
@@ -124,8 +124,8 @@ public sealed class ExtractorProperty<T> : DeclarativePropertyBase<T>
|
||||
}
|
||||
|
||||
public ExtractorProperty(
|
||||
IDeclarativeProperty<ReadOnlyObservableCollection<T>> from,
|
||||
Func<IList<T>?, T?> extractor) : base(extractor(from.Value))
|
||||
IDeclarativeProperty<ReadOnlyObservableCollection<T>?> from,
|
||||
Func<IList<T>?, TResult> extractor) : base(extractor(from.Value))
|
||||
{
|
||||
_extractor = extractor;
|
||||
_collectionWrapper = from.Value is null
|
||||
@@ -136,8 +136,8 @@ public sealed class ExtractorProperty<T> : DeclarativePropertyBase<T>
|
||||
}
|
||||
|
||||
public ExtractorProperty(
|
||||
IDeclarativeProperty<CollectionComputing<T>> from,
|
||||
Func<IList<T>?, T?> extractor) : base(extractor(from.Value))
|
||||
IDeclarativeProperty<CollectionComputing<T>?> from,
|
||||
Func<IList<T>?, TResult> extractor) : base(extractor(from.Value))
|
||||
{
|
||||
_extractor = extractor;
|
||||
_collectionWrapper = from.Value is null
|
||||
@@ -165,9 +165,7 @@ public sealed class ExtractorProperty<T> : DeclarativePropertyBase<T>
|
||||
|
||||
private async Task Fire(IList<T>? items, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var newValue = items is null
|
||||
? default
|
||||
: _extractor(items);
|
||||
var newValue = _extractor(items);
|
||||
|
||||
await SetNewValueAsync(newValue, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -7,13 +7,22 @@ public sealed class FilterProperty<T> : DeclarativePropertyBase<T>
|
||||
public FilterProperty(
|
||||
Func<T?, Task<bool>> filter,
|
||||
IDeclarativeProperty<T> from,
|
||||
Action<T?>? setValueHook = null) : base(setValueHook)
|
||||
Action<T>? setValueHook = null) : base(default!, setValueHook)
|
||||
{
|
||||
_filter = filter;
|
||||
|
||||
var initialValueTask = Task.Run(async () => await _filter(from.Value));
|
||||
initialValueTask.Wait();
|
||||
if (initialValueTask.Result)
|
||||
{
|
||||
SetNewValueSync(from.Value);
|
||||
}
|
||||
// Unfortunately we can't set a default value if the parent current value can not passed by the filter.
|
||||
|
||||
AddDisposable(from.Subscribe(SetValue));
|
||||
}
|
||||
|
||||
private async Task SetValue(T? next, CancellationToken cancellationToken = default)
|
||||
private async Task SetValue(T next, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (await _filter(next))
|
||||
{
|
||||
@@ -24,7 +33,7 @@ public sealed class FilterProperty<T> : DeclarativePropertyBase<T>
|
||||
public static async Task<FilterProperty<T>> CreateAsync(
|
||||
Func<T?, Task<bool>> filter,
|
||||
IDeclarativeProperty<T> from,
|
||||
Action<T?>? setValueHook = null)
|
||||
Action<T>? setValueHook = null)
|
||||
{
|
||||
var prop = new FilterProperty<T>(filter, from, setValueHook);
|
||||
await prop.SetValue(from.Value);
|
||||
|
||||
@@ -4,9 +4,9 @@ namespace DeclarativeProperty;
|
||||
|
||||
public interface IDeclarativeProperty<T> : INotifyPropertyChanged, IDisposable, IObservable<T>
|
||||
{
|
||||
T? Value { get; set; }
|
||||
IDisposable Subscribe(Func<T?, CancellationToken, Task> onChange);
|
||||
void Unsubscribe(Func<T?, CancellationToken, Task> onChange);
|
||||
T Value { get; set; }
|
||||
IDisposable Subscribe(Func<T, CancellationToken, Task> onChange);
|
||||
void Unsubscribe(Func<T, CancellationToken, Task> onChange);
|
||||
|
||||
IDeclarativeProperty<T> RegisterTrigger(
|
||||
Func<IDeclarativeProperty<T>, T, IDisposable?> triggerSubscribe,
|
||||
|
||||
@@ -6,14 +6,19 @@ public sealed class MapProperty<TFrom, TTo> : DeclarativePropertyBase<TTo>
|
||||
|
||||
public MapProperty(
|
||||
Func<TFrom, CancellationToken, Task<TTo>> mapper,
|
||||
IDeclarativeProperty<TFrom?> from,
|
||||
Action<TTo?>? setValueHook = null) : base(setValueHook)
|
||||
IDeclarativeProperty<TFrom> from,
|
||||
Action<TTo>? setValueHook = null) : base(default!, setValueHook)
|
||||
{
|
||||
_mapper = mapper;
|
||||
|
||||
var initialValueTask = Task.Run(async () => await _mapper(from.Value, CancellationToken.None));
|
||||
initialValueTask.Wait();
|
||||
SetNewValueSync(initialValueTask.Result);
|
||||
|
||||
AddDisposable(from.Subscribe(SetValue));
|
||||
}
|
||||
|
||||
private async Task SetValue(TFrom? next, CancellationToken cancellationToken = default)
|
||||
private async Task SetValue(TFrom next, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var newValue = await _mapper(next!, cancellationToken);
|
||||
await SetNewValueAsync(newValue, cancellationToken);
|
||||
@@ -21,8 +26,8 @@ public sealed class MapProperty<TFrom, TTo> : DeclarativePropertyBase<TTo>
|
||||
|
||||
public static async Task<MapProperty<TFrom, TTo>> CreateAsync(
|
||||
Func<TFrom, CancellationToken, Task<TTo>> mapper,
|
||||
IDeclarativeProperty<TFrom?> from,
|
||||
Action<TTo?>? setValueHook = null)
|
||||
IDeclarativeProperty<TFrom> from,
|
||||
Action<TTo>? setValueHook = null)
|
||||
{
|
||||
var prop = new MapProperty<TFrom, TTo>(mapper, from, setValueHook);
|
||||
await prop.SetValue(from.Value);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
public class MergeProperty<T> : DeclarativePropertyBase<T>
|
||||
{
|
||||
public MergeProperty(params IDeclarativeProperty<T>[] props)
|
||||
public MergeProperty(params IDeclarativeProperty<T>[] props) : base(default!)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(props);
|
||||
|
||||
@@ -12,6 +12,6 @@ public class MergeProperty<T> : DeclarativePropertyBase<T>
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateAsync(T? newValue, CancellationToken token)
|
||||
private async Task UpdateAsync(T newValue, CancellationToken token)
|
||||
=> await SetNewValueAsync(newValue, token);
|
||||
}
|
||||
@@ -4,7 +4,7 @@ public sealed class SwitchProperty<TItem> : DeclarativePropertyBase<TItem>
|
||||
{
|
||||
private IDisposable? _innerSubscription;
|
||||
|
||||
public SwitchProperty(IDeclarativeProperty<IDeclarativeProperty<TItem>?> from) : base(from.Value is null ? default : from.Value.Value)
|
||||
public SwitchProperty(IDeclarativeProperty<IDeclarativeProperty<TItem>?> from) : base(from.Value is null ? default! : from.Value.Value)
|
||||
{
|
||||
AddDisposable(from.Subscribe(HandleStreamChange));
|
||||
_innerSubscription = from.Value?.Subscribe(HandleInnerValueChange);
|
||||
@@ -15,10 +15,9 @@ public sealed class SwitchProperty<TItem> : DeclarativePropertyBase<TItem>
|
||||
_innerSubscription?.Dispose();
|
||||
_innerSubscription = next?.Subscribe(HandleInnerValueChange);
|
||||
|
||||
|
||||
await SetNewValueAsync(next is null ? default : next.Value, token);
|
||||
await SetNewValueAsync(next is null ? default! : next.Value, token);
|
||||
}
|
||||
|
||||
private async Task HandleInnerValueChange(TItem? next, CancellationToken token)
|
||||
private async Task HandleInnerValueChange(TItem next, CancellationToken token)
|
||||
=> await SetNewValueAsync(next, token);
|
||||
}
|
||||
@@ -9,13 +9,13 @@ public class ThrottleProperty<T> : DeclarativePropertyBase<T>
|
||||
public ThrottleProperty(
|
||||
IDeclarativeProperty<T> from,
|
||||
Func<TimeSpan> interval,
|
||||
Action<T?>? setValueHook = null) : base(from.Value, setValueHook)
|
||||
Action<T>? setValueHook = null) : base(from.Value, setValueHook)
|
||||
{
|
||||
_interval = interval;
|
||||
AddDisposable(from.Subscribe(SetValue));
|
||||
}
|
||||
|
||||
private async Task SetValue(T? next, CancellationToken cancellationToken = default)
|
||||
private async Task SetValue(T next, CancellationToken cancellationToken = default)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
internal sealed class Unsubscriber<T> : IDisposable
|
||||
{
|
||||
private readonly IDeclarativeProperty<T> _owner;
|
||||
private readonly Func<T?, CancellationToken, Task> _onChange;
|
||||
private readonly Func<T, CancellationToken, Task> _onChange;
|
||||
|
||||
public Unsubscriber(IDeclarativeProperty<T> owner, Func<T?, CancellationToken, Task> onChange)
|
||||
public Unsubscriber(IDeclarativeProperty<T> owner, Func<T, CancellationToken, Task> onChange)
|
||||
{
|
||||
_owner = owner;
|
||||
_onChange = onChange;
|
||||
|
||||
Reference in New Issue
Block a user