Ooui-tws-port/Ooui/Node.cs

239 lines
7.7 KiB
C#
Raw Normal View History

2017-06-12 20:45:27 +00:00
using System;
2018-03-09 20:56:32 +00:00
using System.Collections;
2017-06-12 20:45:27 +00:00
using System.Collections.Generic;
namespace Ooui
{
2017-06-15 01:24:59 +00:00
public abstract class Node : EventTarget
2017-06-12 20:45:27 +00:00
{
readonly List<Node> children = new List<Node> ();
2017-06-17 00:33:27 +00:00
public IReadOnlyList<Node> Children {
get {
lock (children) {
2018-03-09 20:56:32 +00:00
return new ReadOnlyList<Node> (children);
2017-06-17 00:33:27 +00:00
}
}
}
2017-06-13 03:31:47 +00:00
2017-06-24 23:25:14 +00:00
public Node FirstChild {
get {
lock (children) {
if (children.Count > 0)
return children[0];
return null;
}
}
}
2017-06-14 04:17:50 +00:00
public virtual string Text {
2018-03-03 06:14:22 +00:00
get {
var sb = new System.Text.StringBuilder ();
foreach (var c in Children) {
sb.Append (c.Text);
}
return sb.ToString ();
}
2017-06-13 07:51:24 +00:00
set {
2017-06-14 04:17:50 +00:00
ReplaceAll (new TextNode (value ?? ""));
2017-06-13 07:51:24 +00:00
}
}
2017-06-15 06:20:51 +00:00
protected Node (string tagName)
: base (tagName)
2018-03-09 20:56:32 +00:00
{
2017-06-12 20:45:27 +00:00
}
2017-06-15 09:39:19 +00:00
public override EventTarget GetElementById (string id)
{
if (id == Id) return this;
foreach (var c in Children) {
if (c is Element e) {
var r = e.GetElementById (id);
if (r != null)
return r;
}
}
return null;
}
2017-06-12 20:45:27 +00:00
public Node AppendChild (Node newChild)
{
return InsertBefore (newChild, null);
}
public Node InsertBefore (Node newChild, Node referenceChild)
{
2017-06-17 00:33:27 +00:00
if (newChild == null)
return null;
lock (children) {
if (referenceChild == null) {
children.Add (newChild);
}
else {
var index = children.IndexOf (referenceChild);
if (index < 0) {
throw new ArgumentException ("Reference must be a child of this element", nameof (referenceChild));
}
children.Insert (index, newChild);
}
2017-06-12 20:45:27 +00:00
}
2017-06-17 00:33:27 +00:00
newChild.MessageSent += HandleChildMessageSent;
Call ("insertBefore", newChild, referenceChild);
OnChildInsertedBefore (newChild, referenceChild);
2017-06-12 20:45:27 +00:00
return newChild;
}
public Node RemoveChild (Node child)
{
2017-06-17 00:07:30 +00:00
if (child == null)
return null;
2017-06-17 00:33:27 +00:00
lock (children) {
if (!children.Remove (child)) {
2018-03-09 20:56:32 +00:00
throw new ArgumentException ("Child not contained in this element", nameof (child));
2017-06-17 00:33:27 +00:00
}
2017-06-12 20:45:27 +00:00
}
2017-06-17 00:07:30 +00:00
child.MessageSent -= HandleChildMessageSent;
Call ("removeChild", child);
OnChildRemoved (child);
2017-06-12 20:45:27 +00:00
return child;
}
protected virtual void OnChildInsertedBefore (Node newChild, Node referenceChild)
{
}
protected virtual void OnChildRemoved (Node child)
{
}
2017-06-13 07:51:24 +00:00
protected void ReplaceAll (Node newNode)
{
2017-06-17 00:33:27 +00:00
var toRemove = new List<Node> ();
lock (children) {
toRemove.AddRange (children);
children.Clear ();
}
foreach (var child in toRemove) {
child.MessageSent -= HandleChildMessageSent;
Call ("removeChild", child);
2017-06-17 00:33:27 +00:00
}
2017-06-13 07:51:24 +00:00
InsertBefore (newNode, null);
}
2017-06-15 09:39:19 +00:00
2017-06-17 00:07:30 +00:00
void HandleChildMessageSent (Message message)
{
Send (message);
}
2017-06-24 20:34:47 +00:00
protected override bool SaveStateMessageIfNeeded (Message message)
2017-06-15 09:39:19 +00:00
{
2017-06-24 20:34:47 +00:00
if (message.TargetId == Id) {
2018-03-03 06:06:46 +00:00
var handled = false;
2017-06-24 20:34:47 +00:00
switch (message.MessageType) {
case MessageType.Call when message.Key == "insertBefore":
AddStateMessage (message);
break;
case MessageType.Call when message.Key == "removeChild" && message.Value is Array ma && ma.Length == 1:
UpdateStateMessages (state => {
var mchild = ma.GetValue (0);
2018-03-09 20:56:32 +00:00
Node nextChild = null;
for (var i = 0; i < state.Count;) {
2017-06-24 20:34:47 +00:00
var x = state[i];
if (x.Key == "insertBefore" && x.Value is Array xa && xa.Length == 2 && ReferenceEquals (xa.GetValue (0), mchild)) {
// Remove any inserts for this node
nextChild = xa.GetValue (1) as Node;
state.RemoveAt (i);
}
else if (x.Key == "insertBefore" && x.Value is Array ya && ya.Length == 2 && ReferenceEquals (ya.GetValue (1), mchild)) {
// Replace inserts that reference this node
state[i] = Message.Call (Id, "insertBefore", ya.GetValue (0), nextChild);
i++;
}
else {
i++;
}
2017-06-18 06:21:49 +00:00
}
2017-06-24 20:34:47 +00:00
});
break;
2018-03-03 06:06:46 +00:00
}
if (!handled) {
base.SaveStateMessageIfNeeded (message);
2017-06-24 20:34:47 +00:00
}
return true;
}
else {
var ch = Children;
for (var i = 0; i < ch.Count; i++) {
if (ch[i].SaveStateMessageIfNeeded (message))
return true;
}
return false;
2017-06-15 09:39:19 +00:00
}
}
2017-06-24 19:58:14 +00:00
protected override bool TriggerEventFromMessage (Message message)
{
2017-06-24 20:34:47 +00:00
if (message.TargetId == Id) {
if (base.TriggerEventFromMessage (message))
2017-06-24 19:58:14 +00:00
return true;
}
2017-06-24 20:34:47 +00:00
else {
var ch = Children;
for (var i = 0; i < ch.Count; i++) {
if (ch[i].TriggerEventFromMessage (message))
return true;
}
}
2017-06-24 19:58:14 +00:00
return false;
}
2018-02-02 04:18:16 +00:00
#if !NO_XML
2018-02-02 04:18:16 +00:00
public virtual string OuterHtml {
get {
using (var stream = new System.IO.MemoryStream ()) {
var settings = new System.Xml.XmlWriterSettings {
OmitXmlDeclaration = true,
ConformanceLevel = System.Xml.ConformanceLevel.Fragment,
CloseOutput = false,
};
using (var w = System.Xml.XmlWriter.Create (stream, settings)) {
WriteOuterHtml (w);
}
stream.Position = 0;
return new System.IO.StreamReader (stream).ReadToEnd ();
}
}
}
public abstract void WriteOuterHtml (System.Xml.XmlWriter w);
#endif
2017-06-12 20:45:27 +00:00
}
2018-03-09 20:56:32 +00:00
class ReadOnlyList<T> : IReadOnlyList<T>
{
readonly List<T> list;
public ReadOnlyList (List<T> items)
{
list = new List<T> (items);
}
T IReadOnlyList<T>.this[int index] => list[index];
int IReadOnlyCollection<T>.Count => list.Count;
IEnumerator<T> IEnumerable<T>.GetEnumerator ()
{
return ((IEnumerable<T>)list).GetEnumerator ();
}
IEnumerator IEnumerable.GetEnumerator ()
{
return ((IEnumerable)list).GetEnumerator ();
}
}
2017-06-12 20:45:27 +00:00
}