mirror of
https://github.com/ppy/osu.git
synced 2025-02-13 16:02:58 +08:00
Add ability to watch properties via a RealmAccess
helper method
This commit is contained in:
parent
a38eb426ef
commit
35f532fefa
@ -3,9 +3,11 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -318,6 +320,66 @@ namespace osu.Game.Database
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to the property of a realm object to watch for changes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// On subscribing, unless the <paramref name="modelAccessor"/> does not match an object, an initial invocation of <paramref name="onChanged"/> will occur immediately.
|
||||
/// Further invocations will occur when the value changes, but may also fire on a realm recycle with no actual value change.
|
||||
/// </remarks>
|
||||
/// <param name="modelAccessor">A function to retrieve the relevant model from realm.</param>
|
||||
/// <param name="propertyLookup">A function to traverse to the relevant property from the model.</param>
|
||||
/// <param name="onChanged">A function to be invoked when a change of value occurs.</param>
|
||||
/// <typeparam name="TModel">The type of the model.</typeparam>
|
||||
/// <typeparam name="TProperty">The type of the property to be watched.</typeparam>
|
||||
/// <returns>
|
||||
/// A subscription token. It must be kept alive for as long as you want to receive change notifications.
|
||||
/// To stop receiving notifications, call <see cref="IDisposable.Dispose"/>.
|
||||
/// </returns>
|
||||
public IDisposable SubscribeToPropertyChanged<TModel, TProperty>(Func<Realm, TModel?> modelAccessor, Expression<Func<TModel, TProperty>> propertyLookup, Action<TProperty> onChanged)
|
||||
where TModel : RealmObjectBase
|
||||
{
|
||||
return RegisterCustomSubscription(r =>
|
||||
{
|
||||
string propertyName = getMemberName(propertyLookup);
|
||||
|
||||
var model = Run(modelAccessor);
|
||||
var propLookupCompiled = propertyLookup.Compile();
|
||||
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
model.PropertyChanged += onPropertyChanged;
|
||||
|
||||
// Update initial value immediately.
|
||||
onChanged(propLookupCompiled(model));
|
||||
|
||||
return new InvokeOnDisposal(() => model.PropertyChanged -= onPropertyChanged);
|
||||
|
||||
void onPropertyChanged(object sender, PropertyChangedEventArgs args)
|
||||
{
|
||||
if (args.PropertyName == propertyName)
|
||||
onChanged(propLookupCompiled(model));
|
||||
}
|
||||
});
|
||||
|
||||
static string getMemberName(Expression<Func<TModel, TProperty>> expression)
|
||||
{
|
||||
if (!(expression is LambdaExpression lambda))
|
||||
throw new ArgumentException($"Outermost expression must be a lambda expression", nameof(expression));
|
||||
|
||||
if (!(lambda.Body is MemberExpression memberExpression))
|
||||
throw new ArgumentException($"Lambda body must be a member access expression", nameof(expression));
|
||||
|
||||
// TODO: nested access can be supported, with more iteration here
|
||||
// (need to iteratively soft-cast `memberExpression.Expression` into `MemberExpression`s until `lambda.Parameters[0]` is hit)
|
||||
if (memberExpression.Expression != lambda.Parameters[0])
|
||||
throw new ArgumentException($"Nested access expressions are not supported", nameof(expression));
|
||||
|
||||
return memberExpression.Member.Name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run work on realm that will be run every time the update thread realm instance gets recycled.
|
||||
/// </summary>
|
||||
|
Loading…
Reference in New Issue
Block a user