Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
59 changes: 59 additions & 0 deletions Source/Assets/MarkLight/Source/Plugins/AutoSubscription.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using UnityEngine;
using System.Collections.Generic;

namespace MarkLight
{
public static class AutoSubscription
{
static Stack<IAutoSubscriber> AutoSubscriberQueue = new Stack<IAutoSubscriber>();
static HashSet<IAutoSubscriber> AutoSubscribers = new HashSet<IAutoSubscriber>();

public static int ActiveAutoSubscriberCount { get { return AutoSubscriberQueue.Count; } }

public static void StartSubscription(IAutoSubscriber subscriber)
{
if (subscriber == null)
throw new System.ArgumentNullException("subscriber");
if (AutoSubscribers.Contains(subscriber))
throw new System.InvalidOperationException("Recursive calculation detected");

AutoSubscriberQueue.Push(subscriber);
AutoSubscribers.Add(subscriber);
}

public static void EndSubscription(IAutoSubscriber subscriber)
{
if (subscriber == null)
throw new System.ArgumentNullException("subscriber");
if (AutoSubscriberQueue.Count == 0)
throw new System.InvalidOperationException("There are no active IAutoSubscribers");
if (!AutoSubscribers.Contains(subscriber))
throw new System.InvalidOperationException("EndSubscription called on a subscriber without StartSubscription");
if (AutoSubscriberQueue.Peek() != subscriber)
throw new System.InvalidOperationException("EndSubscription is being called for a subscriber that is not at the top of the stack");

AutoSubscribers.Remove(subscriber);
AutoSubscriberQueue.Pop();
}

public static void NotifyViewFieldWasAccessed(ViewFieldBase field)
{
if (AutoSubscriberQueue.Count == 0)
return;

IAutoSubscriber topmostSubscriber = AutoSubscriberQueue.Peek();
topmostSubscriber.ViewFieldWasAccessed(field);
}

public static void NotifyObservableListWasAccessed(IObservableList list)
{
if (AutoSubscriberQueue.Count == 0)
return;

IAutoSubscriber topmostSubscriber = AutoSubscriberQueue.Peek();
topmostSubscriber.ObservableListWasAccessed(list);
}


}
}
127 changes: 127 additions & 0 deletions Source/Assets/MarkLight/Source/Plugins/CalculatedViewField.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using UnityEngine;

namespace MarkLight
{
[Serializable]
public class CalculatedViewField<T> : ViewField<T>, IAutoSubscriber
{
bool IsCalculating;
bool CachedValueIsValid;
HashSet<ViewFieldBase> FieldsSubscribedTo = new HashSet<ViewFieldBase>();
HashSet<IObservableList> ObservableListsSubscribedTo = new HashSet<IObservableList>();
public System.Func<T> GetCalculatedValue;
public System.Action<T> UpdateSourceValuesFromValue;

public override T InternalValue
{
get
{
EnsureCachedValueIsUpToDate();
return base.InternalValue;
}

set
{
base.InternalValue = value;
}
}

public override T Value
{
get
{
EnsureCachedValueIsUpToDate();
return base.Value;
}

set
{
base.Value = value;
}
}

void EnsureCachedValueIsUpToDate()
{
if (!CachedValueIsValid)
UpdateCachedValue();
}

void UpdateCachedValue()
{
CachedValueIsValid = false;
if (GetCalculatedValue == null)
throw new System.InvalidOperationException("GetCalculatedValue has not been specifed");
T calculatedValue;
try
{
IsCalculating = true;
ClearChangeSubscriptions();
AutoSubscription.StartSubscription(this);
calculatedValue = GetCalculatedValue();
}
finally
{
IsCalculating = false;
AutoSubscription.EndSubscription(this);
}
CachedValueIsValid = true;
Value = calculatedValue;
}

void SetValue(T value)
{
if (UpdateSourceValuesFromValue == null)
return;
UpdateSourceValuesFromValue(value);
CachedValueIsValid = false;
}

void IAutoSubscriber.ViewFieldWasAccessed(ViewFieldBase viewField)
{
if (viewField != this && !FieldsSubscribedTo.Contains(viewField))
{
viewField.ValueSet += ViewField_ValueSet;
FieldsSubscribedTo.Add(viewField);
}
}

void IAutoSubscriber.ObservableListWasAccessed(IObservableList list)
{
if (!ObservableListsSubscribedTo.Contains(list))
{
list.ListChanged += List_ListChanged;
ObservableListsSubscribedTo.Add(list);
}
}

private void List_ListChanged(object sender, ListChangedEventArgs e)
{
CachedValueIsValid = false;
if (!IsCalculating)
UpdateCachedValue();
}

void ViewField_ValueSet(object sender, EventArgs e)
{
CachedValueIsValid = false;
if (!IsCalculating)
UpdateCachedValue();
}

void ClearChangeSubscriptions()
{
foreach (ViewFieldBase notifier in FieldsSubscribedTo)
notifier.ValueSet -= ViewField_ValueSet;
FieldsSubscribedTo.Clear();

foreach (IObservableList notifier in ObservableListsSubscribedTo)
notifier.ListChanged -= List_ListChanged;
ObservableListsSubscribedTo.Clear();
}
}

[Serializable]
public class _CalculatedString : CalculatedViewField<string> { }
}
8 changes: 8 additions & 0 deletions Source/Assets/MarkLight/Source/Plugins/IAutoSubscriber.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace MarkLight
{
public interface IAutoSubscriber
{
void ViewFieldWasAccessed(ViewFieldBase viewField);
void ObservableListWasAccessed(IObservableList list);
}
}
Loading