您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
538 行
15 KiB
538 行
15 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UIWidgets.foundation;
|
|
using UIWidgets.rendering;
|
|
using UIWidgets.ui;
|
|
using UnityEngine.Assertions;
|
|
|
|
namespace UIWidgets.widgets {
|
|
public delegate void ElementVisitor(Element element);
|
|
public delegate bool ElementVisitorBool(Element element);
|
|
|
|
|
|
|
|
public abstract class Widget {
|
|
protected Widget(string key) {
|
|
this._key = key;
|
|
}
|
|
|
|
public readonly string _key;
|
|
|
|
public string key {
|
|
get { return this._key; }
|
|
}
|
|
|
|
public abstract Element createElement();
|
|
|
|
public static bool canUpdate(Widget oldWidget, Widget newWidget) {
|
|
return oldWidget.GetType() == newWidget.GetType() && oldWidget.key == newWidget.key;
|
|
}
|
|
}
|
|
|
|
public abstract class StatelessWidget : Widget {
|
|
protected StatelessWidget(string key) : base(key) {
|
|
}
|
|
|
|
public override Element createElement() {
|
|
return new StatelessElement(this);
|
|
}
|
|
|
|
public abstract Widget build(BuildContext context);
|
|
}
|
|
|
|
public abstract class RenderObjectWidget : Widget {
|
|
protected RenderObjectWidget(string key) : base(key) {
|
|
}
|
|
|
|
public abstract RenderObject createRenderObject(BuildContext context);
|
|
|
|
public virtual void updateRenderObject(BuildContext context, RenderObject renderObject) {
|
|
}
|
|
|
|
public virtual void didUnmountRenderObject(RenderObject renderObject) {
|
|
}
|
|
}
|
|
|
|
public abstract class Element : BuildContext {
|
|
protected Element(Widget widget) {
|
|
Assert.IsNotNull(widget);
|
|
this._widget = widget;
|
|
}
|
|
|
|
public Element _parent;
|
|
|
|
public override bool Equals(object obj) {
|
|
return object.ReferenceEquals(this, obj);
|
|
}
|
|
|
|
private static int _nextHashCode = 1;
|
|
|
|
private readonly int _cachedHash = Element._nextHashCode = (Element._nextHashCode + 1) % 0xffffff;
|
|
|
|
public override int GetHashCode() {
|
|
return this._cachedHash;
|
|
}
|
|
|
|
public object _slot;
|
|
|
|
public object slot {
|
|
get { return this._slot; }
|
|
}
|
|
|
|
public int _depth;
|
|
|
|
public int depth {
|
|
get { return this._depth; }
|
|
}
|
|
|
|
public static int _sort(Element a, Element b) {
|
|
if (a.depth < b.depth)
|
|
return -1;
|
|
if (b.depth < a.depth)
|
|
return 1;
|
|
if (b.dirty && !a.dirty)
|
|
return -1;
|
|
if (a.dirty && !b.dirty)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
public Widget _widget;
|
|
|
|
public Widget widget {
|
|
get { return this._widget; }
|
|
}
|
|
|
|
public BuildOwner _owner;
|
|
|
|
public BuildOwner owner {
|
|
get { return this._owner; }
|
|
}
|
|
|
|
public bool _active = false;
|
|
|
|
public RenderObject renderObject {
|
|
get {
|
|
RenderObject result = null;
|
|
|
|
ElementVisitor visit = null;
|
|
visit = (element) => {
|
|
Assert.IsNull(result);
|
|
if (element is RenderObjectElement) {
|
|
result = element.renderObject;
|
|
} else {
|
|
element.visitChildren(visit);
|
|
}
|
|
};
|
|
|
|
visit(this);
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public virtual void visitChildren(ElementVisitor visitor) {
|
|
}
|
|
|
|
public void visitChildElements(ElementVisitor visitor) {
|
|
this.visitChildren(visitor);
|
|
}
|
|
|
|
public Element updateChild(Element child, Widget newWidget, object newSlot) {
|
|
if (newWidget == null) {
|
|
if (child != null) {
|
|
this.deactivateChild(child);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
if (child != null) {
|
|
if (child.widget == newWidget) {
|
|
if (child.slot != newSlot) {
|
|
this.updateSlotForChild(child, newSlot);
|
|
}
|
|
|
|
return child;
|
|
}
|
|
|
|
if (Widget.canUpdate(child.widget, newWidget)) {
|
|
if (child.slot != newSlot) {
|
|
this.updateSlotForChild(child, newSlot);
|
|
}
|
|
|
|
child.update(newWidget);
|
|
return child;
|
|
}
|
|
|
|
this.deactivateChild(child);
|
|
}
|
|
|
|
return inflateWidget(newWidget, newSlot);
|
|
}
|
|
|
|
public virtual void update(Widget newWidget) {
|
|
this._widget = newWidget;
|
|
}
|
|
|
|
public virtual void mount(Element parent, object newSlot) {
|
|
this._parent = parent;
|
|
this._slot = newSlot;
|
|
this._depth = parent != null ? parent.depth + 1 : 1;
|
|
this._active = true;
|
|
if (parent != null) {
|
|
this._owner = parent.owner;
|
|
}
|
|
|
|
// _updateInheritance();
|
|
}
|
|
|
|
void updateSlotForChild(Element child, object newSlot) {
|
|
ElementVisitor visit = null;
|
|
visit = (element) => {
|
|
element._updateSlot(newSlot);
|
|
if (element is RenderObjectElement) {
|
|
element.visitChildren(visit);
|
|
}
|
|
};
|
|
visit(child);
|
|
}
|
|
|
|
void _updateSlot(object newSlot) {
|
|
this._slot = newSlot;
|
|
}
|
|
|
|
void _updateDepth(int parentDepth) {
|
|
int expectedDepth = parentDepth + 1;
|
|
if (this._depth < expectedDepth) {
|
|
this._depth = expectedDepth;
|
|
this.visitChildren(child => { child._updateDepth(expectedDepth); });
|
|
}
|
|
}
|
|
|
|
|
|
public void detachRenderObject() {
|
|
this.visitChildren(child => { child.detachRenderObject(); });
|
|
this._slot = null;
|
|
}
|
|
|
|
public void attachRenderObject(object newSlot) {
|
|
Assert.IsNull(_slot);
|
|
this.visitChildren(child => { child.attachRenderObject(newSlot); });
|
|
this._slot = newSlot;
|
|
}
|
|
|
|
public Element inflateWidget(Widget newWidget, object newSlot) {
|
|
Element newChild = newWidget.createElement();
|
|
newChild.mount(this, newSlot);
|
|
return newChild;
|
|
}
|
|
|
|
public void deactivateChild(Element child) {
|
|
child._parent = null;
|
|
child.detachRenderObject();
|
|
this.owner._inactiveElements.add(child);
|
|
}
|
|
|
|
public abstract void forgetChild(Element child);
|
|
|
|
public void activate() {
|
|
Assert.IsTrue(!this._active);
|
|
this._active = true;
|
|
if (this._dirty) {
|
|
this.owner.scheduleBuildFor(this);
|
|
}
|
|
}
|
|
|
|
public void deactivate() {
|
|
Assert.IsTrue(this._active);
|
|
this._active = false;
|
|
}
|
|
|
|
public void unmount() {
|
|
}
|
|
|
|
public RenderObject findRenderObject() {
|
|
return this.renderObject;
|
|
}
|
|
|
|
public void didChangeDependencies() {
|
|
this.markNeedsBuild();
|
|
}
|
|
|
|
public void visitAncestorElements(ElementVisitorBool visitor) {
|
|
Element ancestor = this._parent;
|
|
while (ancestor != null && visitor(ancestor)) {
|
|
ancestor = ancestor._parent;
|
|
}
|
|
}
|
|
|
|
public bool _dirty;
|
|
|
|
public bool dirty {
|
|
get { return this._dirty; }
|
|
}
|
|
|
|
public bool _inDirtyList = false;
|
|
|
|
public void markNeedsBuild() {
|
|
if (!this._active) {
|
|
return;
|
|
}
|
|
|
|
if (this.dirty) {
|
|
return;
|
|
}
|
|
|
|
this._dirty = true;
|
|
this.owner.scheduleBuildFor(this);
|
|
}
|
|
|
|
public void rebuild() {
|
|
if (!this._active || !this._dirty) {
|
|
return;
|
|
}
|
|
|
|
this.performRebuild();
|
|
Assert.IsTrue(!this._dirty);
|
|
}
|
|
|
|
public abstract void performRebuild();
|
|
}
|
|
|
|
|
|
public abstract class ComponentElement : Element {
|
|
protected ComponentElement(Widget widget) : base(widget) {
|
|
}
|
|
|
|
public Element _child;
|
|
|
|
public override void mount(Element parent, object newSlot) {
|
|
base.mount(parent, newSlot);
|
|
|
|
this._firstBuild();
|
|
}
|
|
|
|
public void _firstBuild() {
|
|
this.rebuild();
|
|
}
|
|
|
|
public override void performRebuild() {
|
|
Widget built;
|
|
|
|
try {
|
|
built = this.build();
|
|
}
|
|
catch (Exception e) {
|
|
built = null; // ErrorWidget.builder(_debugReportException('building $this', e, stack));
|
|
}
|
|
finally {
|
|
this._dirty = false;
|
|
}
|
|
|
|
try {
|
|
this._child = this.updateChild(this._child, built, this.slot);
|
|
Assert.IsNotNull(_child);
|
|
}
|
|
catch (Exception e) {
|
|
built = null; //ErrorWidget.builder(_debugReportException('building $this', e, stack));
|
|
this._child = this.updateChild(null, built, this.slot);
|
|
}
|
|
}
|
|
|
|
public abstract Widget build();
|
|
|
|
public override void visitChildren(ElementVisitor visitor) {
|
|
if (this._child != null) {
|
|
visitor(this._child);
|
|
}
|
|
}
|
|
|
|
public override void forgetChild(Element child) {
|
|
Assert.IsTrue(child == _child);
|
|
this._child = null;
|
|
}
|
|
}
|
|
|
|
public class StatelessElement : ComponentElement {
|
|
public StatelessElement(StatelessWidget widget) : base(widget) {
|
|
}
|
|
|
|
public new StatelessWidget widget {
|
|
get { return (StatelessWidget) base.widget; }
|
|
}
|
|
|
|
public override Widget build() {
|
|
return this.widget.build(this);
|
|
}
|
|
|
|
public override void update(Widget newWidget) {
|
|
base.update(newWidget);
|
|
|
|
this._dirty = true;
|
|
this.rebuild();
|
|
}
|
|
}
|
|
|
|
public abstract class RenderObjectElement : Element {
|
|
protected RenderObjectElement(RenderObjectWidget widget) : base(widget) {
|
|
}
|
|
|
|
public new RenderObjectWidget widget {
|
|
get { return (RenderObjectWidget) base.widget; }
|
|
}
|
|
|
|
public RenderObject _renderObject;
|
|
|
|
public RenderObject renderObject {
|
|
get { return this._renderObject; }
|
|
}
|
|
|
|
public RenderObjectElement _ancestorRenderObjectElement;
|
|
|
|
public override void mount(Element parent, object newSlot) {
|
|
this._renderObject = this.widget.createRenderObject(this);
|
|
this.attachRenderObject(newSlot);
|
|
this._dirty = false;
|
|
}
|
|
|
|
public override void update(Widget newWidget) {
|
|
base.update(newWidget);
|
|
|
|
this.widget.updateRenderObject(this, this.renderObject);
|
|
this._dirty = false;
|
|
}
|
|
|
|
public override void performRebuild() {
|
|
this.widget.updateRenderObject(this, this.renderObject);
|
|
this._dirty = false;
|
|
}
|
|
|
|
public List<Element> updateChildren(List<Element> oldChildren, List<Widget> newWidgets,
|
|
HashSet<Element> forgottenChildren) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public interface BuildContext {
|
|
Widget widget { get; }
|
|
}
|
|
|
|
public class BuildOwner {
|
|
public VoidCallback onBuildScheduled;
|
|
|
|
public BuildOwner(VoidCallback onBuildScheduled) {
|
|
this.onBuildScheduled = onBuildScheduled;
|
|
}
|
|
|
|
public readonly _InactiveElements _inactiveElements = new _InactiveElements();
|
|
public readonly List<Element> _dirtyElements = new List<Element>();
|
|
|
|
public bool _scheduledFlushDirtyElements = false;
|
|
public bool _dirtyElementsNeedsResorting = false;
|
|
|
|
public void scheduleBuildFor(Element element) {
|
|
if (element._inDirtyList) {
|
|
this._dirtyElementsNeedsResorting = true;
|
|
return;
|
|
}
|
|
|
|
if (!this._scheduledFlushDirtyElements && this.onBuildScheduled != null) {
|
|
this._scheduledFlushDirtyElements = true;
|
|
this.onBuildScheduled();
|
|
}
|
|
|
|
this._dirtyElements.Add(element);
|
|
element._inDirtyList = true;
|
|
}
|
|
|
|
public void buildScope(Element context, VoidCallback callback = null) {
|
|
if (callback == null && this._dirtyElements.Count == 0) {
|
|
return;
|
|
}
|
|
|
|
|
|
try {
|
|
this._scheduledFlushDirtyElements = true;
|
|
if (callback != null) {
|
|
this._dirtyElementsNeedsResorting = false;
|
|
callback();
|
|
}
|
|
|
|
this._dirtyElements.Sort(Element._sort);
|
|
this._dirtyElementsNeedsResorting = false;
|
|
|
|
int dirtyCount = this._dirtyElements.Count;
|
|
int index = 0;
|
|
|
|
while (index < dirtyCount) {
|
|
this._dirtyElements[index].rebuild();
|
|
index++;
|
|
|
|
if (dirtyCount < this._dirtyElements.Count || this._dirtyElementsNeedsResorting) {
|
|
this._dirtyElements.Sort(Element._sort);
|
|
this._dirtyElementsNeedsResorting = false;
|
|
dirtyCount = this._dirtyElements.Count;
|
|
while (index > 0 && this._dirtyElements[index - 1].dirty) {
|
|
index -= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally {
|
|
foreach (Element element in this._dirtyElements) {
|
|
Assert.IsTrue(element._inDirtyList);
|
|
element._inDirtyList = false;
|
|
}
|
|
|
|
this._dirtyElements.Clear();
|
|
this._scheduledFlushDirtyElements = false;
|
|
this._dirtyElementsNeedsResorting = false;
|
|
}
|
|
}
|
|
|
|
public void finalizeTree() {
|
|
this._inactiveElements._unmountAll();
|
|
}
|
|
}
|
|
|
|
public class _InactiveElements {
|
|
public readonly HashSet<Element> _elements = new HashSet<Element>();
|
|
|
|
public void _unmount(Element element) {
|
|
element.visitChildren(child => {
|
|
Assert.IsTrue(child._parent.Equals(element));
|
|
this._unmount(child);
|
|
});
|
|
element.unmount();
|
|
}
|
|
|
|
public void _unmountAll() {
|
|
List<Element> elements = this._elements.ToList();
|
|
this._elements.Clear();
|
|
|
|
elements.Sort(Element._sort);
|
|
elements.Reverse();
|
|
|
|
elements.ForEach(this._unmount);
|
|
}
|
|
|
|
public void _deactivateRecursively(Element element) {
|
|
element.deactivate();
|
|
element.visitChildren(this._deactivateRecursively);
|
|
}
|
|
|
|
public void add(Element element) {
|
|
if (element._active) {
|
|
this._deactivateRecursively(element);
|
|
}
|
|
|
|
this._elements.Add(element);
|
|
}
|
|
|
|
public void remove(Element element) {
|
|
this._elements.Remove(element);
|
|
}
|
|
}
|
|
}
|