2017-06-15 01:24:59 +00:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
2018-03-09 20:56:32 +00:00
|
|
|
using System.Reflection;
|
2018-04-19 00:14:21 +00:00
|
|
|
using System.ComponentModel;
|
2017-06-15 01:24:59 +00:00
|
|
|
|
|
|
|
namespace Ooui
|
|
|
|
{
|
2017-06-15 09:39:19 +00:00
|
|
|
[Newtonsoft.Json.JsonConverter (typeof (EventTargetJsonConverter))]
|
2018-04-19 00:14:21 +00:00
|
|
|
public abstract class EventTarget : INotifyPropertyChanged
|
2017-06-15 01:24:59 +00:00
|
|
|
{
|
2017-06-15 06:10:58 +00:00
|
|
|
readonly List<Message> stateMessages = new List<Message> ();
|
|
|
|
|
2017-07-08 05:55:04 +00:00
|
|
|
readonly Dictionary<string, List<TargetEventHandler>> eventListeners =
|
|
|
|
new Dictionary<string, List<TargetEventHandler>> ();
|
2017-06-15 06:10:58 +00:00
|
|
|
|
2018-04-16 20:52:12 +00:00
|
|
|
public string Id { get; protected set; } = GenerateId ();
|
2017-06-15 01:24:59 +00:00
|
|
|
|
2017-06-15 06:20:51 +00:00
|
|
|
public string TagName { get; private set; }
|
2017-06-15 01:24:59 +00:00
|
|
|
|
2018-04-19 00:14:21 +00:00
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
|
2017-06-15 01:24:59 +00:00
|
|
|
public event Action<Message> MessageSent;
|
|
|
|
|
2017-06-18 06:21:49 +00:00
|
|
|
public IReadOnlyList<Message> StateMessages {
|
2017-06-16 00:28:49 +00:00
|
|
|
get {
|
|
|
|
lock (stateMessages) {
|
2018-03-09 20:56:32 +00:00
|
|
|
return new ReadOnlyList<Message> (stateMessages);
|
2017-06-16 00:28:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-06-15 01:24:59 +00:00
|
|
|
|
2017-06-15 06:20:51 +00:00
|
|
|
protected EventTarget (string tagName)
|
2017-06-15 01:24:59 +00:00
|
|
|
{
|
2017-06-15 06:20:51 +00:00
|
|
|
TagName = tagName;
|
2017-06-15 06:30:48 +00:00
|
|
|
|
2017-06-15 06:27:28 +00:00
|
|
|
Send (new Message {
|
|
|
|
MessageType = MessageType.Create,
|
|
|
|
TargetId = Id,
|
|
|
|
Key = TagName,
|
|
|
|
});
|
2017-06-15 01:24:59 +00:00
|
|
|
}
|
|
|
|
|
2017-06-18 06:21:49 +00:00
|
|
|
public override string ToString() => $"<{TagName} id=\"{Id}\" />";
|
|
|
|
|
2017-06-15 09:39:19 +00:00
|
|
|
public virtual EventTarget GetElementById (string id)
|
|
|
|
{
|
|
|
|
if (id == Id) return this;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-07-08 05:55:04 +00:00
|
|
|
public void AddEventListener (string eventType, TargetEventHandler handler)
|
2017-06-15 06:10:58 +00:00
|
|
|
{
|
|
|
|
if (eventType == null) return;
|
|
|
|
if (handler == null) return;
|
|
|
|
|
|
|
|
var sendListen = false;
|
|
|
|
|
2017-07-08 05:55:04 +00:00
|
|
|
List<TargetEventHandler> handlers;
|
2017-06-16 00:50:57 +00:00
|
|
|
lock (eventListeners) {
|
|
|
|
if (!eventListeners.TryGetValue (eventType, out handlers)) {
|
2017-07-08 05:55:04 +00:00
|
|
|
handlers = new List<TargetEventHandler> ();
|
2017-06-16 00:50:57 +00:00
|
|
|
eventListeners[eventType] = handlers;
|
|
|
|
sendListen = true;
|
|
|
|
}
|
|
|
|
handlers.Add (handler);
|
2017-06-15 06:10:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sendListen)
|
2017-06-15 06:30:48 +00:00
|
|
|
Send (new Message {
|
|
|
|
MessageType = MessageType.Listen,
|
|
|
|
TargetId = Id,
|
|
|
|
Key = eventType,
|
|
|
|
});
|
2017-06-15 06:10:58 +00:00
|
|
|
}
|
|
|
|
|
2017-07-08 05:55:04 +00:00
|
|
|
public void RemoveEventListener (string eventType, TargetEventHandler handler)
|
2017-06-15 06:10:58 +00:00
|
|
|
{
|
|
|
|
if (eventType == null) return;
|
|
|
|
if (handler == null) return;
|
|
|
|
|
2017-07-08 05:55:04 +00:00
|
|
|
List<TargetEventHandler> handlers;
|
2017-06-16 00:50:57 +00:00
|
|
|
lock (eventListeners) {
|
|
|
|
if (eventListeners.TryGetValue (eventType, out handlers)) {
|
|
|
|
handlers.Remove (handler);
|
|
|
|
}
|
2017-06-15 06:10:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-02 02:43:23 +00:00
|
|
|
protected bool SetProperty<T> (ref T backingStore, T newValue, string jsPropertyName, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
|
2017-06-15 01:24:59 +00:00
|
|
|
{
|
2017-06-18 19:52:51 +00:00
|
|
|
if (EqualityComparer<T>.Default.Equals (backingStore, newValue))
|
|
|
|
return false;
|
|
|
|
backingStore = newValue;
|
2018-02-02 02:43:23 +00:00
|
|
|
SendSet (jsPropertyName, newValue);
|
2017-06-20 04:55:20 +00:00
|
|
|
OnPropertyChanged (propertyName);
|
2017-06-18 19:52:51 +00:00
|
|
|
return true;
|
2017-06-15 01:24:59 +00:00
|
|
|
}
|
|
|
|
|
2017-06-20 04:55:20 +00:00
|
|
|
protected virtual void OnPropertyChanged (string propertyName)
|
|
|
|
{
|
2018-04-19 00:14:21 +00:00
|
|
|
PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (propertyName));
|
2017-06-20 04:55:20 +00:00
|
|
|
}
|
|
|
|
|
2017-06-15 09:39:19 +00:00
|
|
|
public const char IdPrefix = '\u2999';
|
|
|
|
|
2017-06-15 01:24:59 +00:00
|
|
|
static long idCounter = 0;
|
|
|
|
static string GenerateId ()
|
|
|
|
{
|
|
|
|
var id = System.Threading.Interlocked.Increment (ref idCounter);
|
2017-06-15 09:39:19 +00:00
|
|
|
return $"{IdPrefix}{id}";
|
2017-06-15 01:24:59 +00:00
|
|
|
}
|
|
|
|
|
2017-06-17 00:07:30 +00:00
|
|
|
public void Send (Message message)
|
2017-06-15 01:24:59 +00:00
|
|
|
{
|
2017-06-17 00:07:30 +00:00
|
|
|
if (message == null)
|
|
|
|
return;
|
|
|
|
if (message.TargetId == Id)
|
|
|
|
SaveStateMessageIfNeeded (message);
|
2017-06-15 01:24:59 +00:00
|
|
|
MessageSent?.Invoke (message);
|
|
|
|
}
|
|
|
|
|
2017-12-10 22:21:49 +00:00
|
|
|
public void Call (string methodName, params object[] args)
|
2017-06-15 01:24:59 +00:00
|
|
|
{
|
2017-06-16 04:27:15 +00:00
|
|
|
Send (Message.Call (Id, methodName, args));
|
2017-06-15 01:24:59 +00:00
|
|
|
}
|
|
|
|
|
2018-02-02 02:43:23 +00:00
|
|
|
protected void SendSet (string jsPropertyName, object value)
|
2017-06-15 01:24:59 +00:00
|
|
|
{
|
|
|
|
Send (new Message {
|
|
|
|
MessageType = MessageType.Set,
|
|
|
|
TargetId = Id,
|
2018-02-02 02:43:23 +00:00
|
|
|
Key = jsPropertyName,
|
2017-06-15 01:24:59 +00:00
|
|
|
Value = value,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-06-17 00:07:30 +00:00
|
|
|
public void Receive (Message message)
|
2017-06-15 01:24:59 +00:00
|
|
|
{
|
|
|
|
if (message == null)
|
|
|
|
return;
|
2017-06-24 20:34:47 +00:00
|
|
|
SaveStateMessageIfNeeded (message);
|
2017-06-15 01:24:59 +00:00
|
|
|
TriggerEventFromMessage (message);
|
|
|
|
}
|
|
|
|
|
2017-06-18 06:21:49 +00:00
|
|
|
protected void AddStateMessage (Message message)
|
2017-06-15 01:24:59 +00:00
|
|
|
{
|
2017-06-16 00:28:49 +00:00
|
|
|
lock (stateMessages) stateMessages.Add (message);
|
2017-06-15 01:24:59 +00:00
|
|
|
}
|
|
|
|
|
2017-06-18 06:21:49 +00:00
|
|
|
protected void UpdateStateMessages (Action<List<Message>> updater)
|
2017-06-15 01:24:59 +00:00
|
|
|
{
|
2017-06-18 06:21:49 +00:00
|
|
|
lock (stateMessages) updater (stateMessages);
|
2017-06-15 01:24:59 +00:00
|
|
|
}
|
|
|
|
|
2017-06-24 20:34:47 +00:00
|
|
|
protected virtual bool SaveStateMessageIfNeeded (Message message)
|
2017-06-15 01:24:59 +00:00
|
|
|
{
|
2017-06-24 20:34:47 +00:00
|
|
|
if (message.TargetId != Id)
|
|
|
|
return false;
|
|
|
|
|
2017-06-15 01:24:59 +00:00
|
|
|
switch (message.MessageType) {
|
|
|
|
case MessageType.Create:
|
2017-06-18 06:21:49 +00:00
|
|
|
AddStateMessage (message);
|
2017-06-15 01:24:59 +00:00
|
|
|
break;
|
|
|
|
case MessageType.Set:
|
2017-06-18 06:21:49 +00:00
|
|
|
UpdateStateMessages (state => {
|
|
|
|
state.RemoveAll (x => x.MessageType == MessageType.Set && x.Key == message.Key);
|
|
|
|
state.Add (message);
|
|
|
|
});
|
2017-11-26 17:28:06 +00:00
|
|
|
break;
|
|
|
|
case MessageType.SetAttribute:
|
|
|
|
UpdateStateMessages (state => {
|
|
|
|
state.RemoveAll (x => x.MessageType == MessageType.SetAttribute && x.Key == message.Key);
|
|
|
|
state.Add (message);
|
|
|
|
});
|
2017-06-15 01:24:59 +00:00
|
|
|
break;
|
2018-02-02 02:43:23 +00:00
|
|
|
case MessageType.RemoveAttribute:
|
|
|
|
this.UpdateStateMessages (state => {
|
|
|
|
state.RemoveAll (x => x.MessageType == MessageType.SetAttribute && x.Key == message.Key);
|
|
|
|
});
|
|
|
|
return true;
|
2017-06-15 06:10:58 +00:00
|
|
|
case MessageType.Listen:
|
2017-06-18 06:21:49 +00:00
|
|
|
AddStateMessage (message);
|
2017-06-15 06:10:58 +00:00
|
|
|
break;
|
2017-06-15 01:24:59 +00:00
|
|
|
}
|
2017-06-24 20:34:47 +00:00
|
|
|
|
|
|
|
return true;
|
2017-06-15 01:24:59 +00:00
|
|
|
}
|
|
|
|
|
2018-02-03 05:03:42 +00:00
|
|
|
protected virtual bool TriggerEvent (string name)
|
|
|
|
{
|
|
|
|
List<TargetEventHandler> handlers = null;
|
|
|
|
lock (eventListeners) {
|
|
|
|
List<TargetEventHandler> hs;
|
|
|
|
if (eventListeners.TryGetValue (name, out hs)) {
|
|
|
|
handlers = new List<TargetEventHandler> (hs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (handlers != null) {
|
|
|
|
var args = new TargetEventArgs ();
|
|
|
|
foreach (var h in handlers) {
|
|
|
|
h.Invoke (this, args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-06-24 19:58:14 +00:00
|
|
|
protected virtual bool TriggerEventFromMessage (Message message)
|
2017-06-15 01:24:59 +00:00
|
|
|
{
|
2017-06-24 19:58:14 +00:00
|
|
|
if (message.TargetId != Id)
|
|
|
|
return false;
|
2017-06-24 20:34:47 +00:00
|
|
|
|
2017-07-08 05:55:04 +00:00
|
|
|
List<TargetEventHandler> handlers = null;
|
2017-06-16 00:50:57 +00:00
|
|
|
lock (eventListeners) {
|
2017-07-08 05:55:04 +00:00
|
|
|
List<TargetEventHandler> hs;
|
2017-06-16 00:50:57 +00:00
|
|
|
if (eventListeners.TryGetValue (message.Key, out hs)) {
|
2017-07-08 05:55:04 +00:00
|
|
|
handlers = new List<TargetEventHandler> (hs);
|
2017-06-15 06:10:58 +00:00
|
|
|
}
|
|
|
|
}
|
2017-06-24 20:34:47 +00:00
|
|
|
if (handlers != null) {
|
2017-07-08 05:55:04 +00:00
|
|
|
var args = new TargetEventArgs ();
|
|
|
|
if (message.Value is Newtonsoft.Json.Linq.JObject o) {
|
2018-11-14 15:18:22 +00:00
|
|
|
if (o["offsetX"] != null)
|
|
|
|
{
|
|
|
|
args.OffsetX = (double)o["offsetX"];
|
|
|
|
args.OffsetY = (double)o["offsetY"];
|
|
|
|
}
|
|
|
|
if (o["clientHeight"] != null)
|
|
|
|
{
|
|
|
|
args.ClientHeight = (double)o.GetValue("clientHeight");
|
|
|
|
args.ClientWidth = (double)o.GetValue("clientWidth");
|
|
|
|
}
|
2017-07-08 05:55:04 +00:00
|
|
|
}
|
2017-06-24 20:34:47 +00:00
|
|
|
foreach (var h in handlers) {
|
|
|
|
h.Invoke (this, args);
|
|
|
|
}
|
2017-06-16 00:50:57 +00:00
|
|
|
}
|
2017-06-24 19:58:14 +00:00
|
|
|
return true;
|
2017-06-15 01:24:59 +00:00
|
|
|
}
|
|
|
|
}
|
2017-06-15 09:39:19 +00:00
|
|
|
|
|
|
|
class EventTargetJsonConverter : Newtonsoft.Json.JsonConverter
|
|
|
|
{
|
|
|
|
public override bool CanRead => false;
|
|
|
|
|
|
|
|
public override void WriteJson (Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
|
|
|
|
{
|
|
|
|
writer.WriteValue (((EventTarget)value).Id);
|
|
|
|
}
|
|
|
|
|
|
|
|
public override object ReadJson (Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
|
|
|
|
{
|
2017-06-17 00:07:30 +00:00
|
|
|
throw new NotSupportedException ();
|
2017-06-15 09:39:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public override bool CanConvert (Type objectType)
|
|
|
|
{
|
2018-03-09 20:56:32 +00:00
|
|
|
return typeof (EventTarget).GetTypeInfo ().IsAssignableFrom (objectType.GetTypeInfo ());
|
2017-06-15 09:39:19 +00:00
|
|
|
}
|
|
|
|
}
|
2017-07-08 05:55:04 +00:00
|
|
|
|
|
|
|
public delegate void TargetEventHandler (object sender, TargetEventArgs e);
|
|
|
|
|
|
|
|
public class TargetEventArgs : EventArgs
|
|
|
|
{
|
|
|
|
public double OffsetX { get; set; }
|
|
|
|
public double OffsetY { get; set; }
|
2018-11-14 15:18:22 +00:00
|
|
|
public double ClientHeight { get; set; }
|
|
|
|
public double ClientWidth { get; set; }
|
2017-07-08 05:55:04 +00:00
|
|
|
}
|
2017-06-15 01:24:59 +00:00
|
|
|
}
|