|
|
|
|
|
|
using Unity.UIWidgets.rendering; |
|
|
|
using Unity.UIWidgets.scheduler2; |
|
|
|
using Unity.UIWidgets.ui; |
|
|
|
using UnityEngine; |
|
|
|
using Rect = Unity.UIWidgets.ui.Rect; |
|
|
|
|
|
|
|
namespace Unity.UIWidgets.widgets { |
|
|
|
public class OverlayEntry { |
|
|
|
|
|
|
|
|
|
|
class _OverlayEntryWidgetState : State<_OverlayEntryWidget> { |
|
|
|
public override Widget build(BuildContext context) { |
|
|
|
return widget.entry.builder(context); |
|
|
|
return new TickerMode( |
|
|
|
enabled: widget.tickerEnabled, |
|
|
|
child: widget.entry.builder(context) |
|
|
|
); |
|
|
|
/* the state that changed is in the builder */ |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
public readonly List<OverlayEntry> initialEntries; |
|
|
|
|
|
|
|
public static OverlayState of(BuildContext context, Widget debugRequiredFor = null) { |
|
|
|
OverlayState result = (OverlayState) context.ancestorStateOfType(new TypeMatcher<OverlayState>()); |
|
|
|
public static OverlayState of(BuildContext context, bool rootOverlay = false, Widget debugRequiredFor = null) { |
|
|
|
OverlayState result = rootOverlay |
|
|
|
? context.findRootAncestorStateOfType<OverlayState>() |
|
|
|
: context.findAncestorStateOfType<OverlayState>(); |
|
|
|
var additional = context.widget != debugRequiredFor |
|
|
|
? $"\nThe context from which that widget was searching for an overlay was:\n {context}" |
|
|
|
: ""; |
|
|
|
throw new UIWidgetsError( |
|
|
|
"No Overlay widget found.\n" + |
|
|
|
$"{debugRequiredFor.GetType()} widgets require an Overlay widget ancestor for correct operation.\n" + |
|
|
|
"The most common way to add an Overlay to an application is to include a MaterialApp or Navigator widget in the runApp() call.\n" + |
|
|
|
"The specific widget that failed to find an overlay was:\n" + |
|
|
|
$" {debugRequiredFor}" + |
|
|
|
$"{additional}" |
|
|
|
); |
|
|
|
List<DiagnosticsNode> information = new List<DiagnosticsNode>() { |
|
|
|
new ErrorSummary("No Overlay widget found."), |
|
|
|
new ErrorDescription($"{debugRequiredFor.GetType()} widgets require an Overlay widget ancestor for correct operation."), |
|
|
|
new ErrorHint("The most common way to add an Overlay to an application is to include a MaterialApp or Navigator widget in the runApp() call."), |
|
|
|
new DiagnosticsProperty<Widget>("The specific widget that failed to find an overlay was", debugRequiredFor, style: DiagnosticsTreeStyle.errorProperty) |
|
|
|
}; |
|
|
|
|
|
|
|
if (context.widget != debugRequiredFor) { |
|
|
|
information.Add(context.describeElement("The context from which that widget was searching for an overlay was")); |
|
|
|
} |
|
|
|
|
|
|
|
throw new UIWidgetsError(information); |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
{ |
|
|
|
List<OverlayEntry> newEntriesList = |
|
|
|
newEntries is List<OverlayEntry> ? (newEntries as List<OverlayEntry>) : newEntries.ToList(); |
|
|
|
/*D.assert(above == null || below == null, () => "Only one of `above` and `below` may be specified."); |
|
|
|
D.assert(above == null || (above._overlay == this && _entries.Contains(above)), |
|
|
|
() => "The provided entry for `above` is not present in the Overlay."); |
|
|
|
D.assert(below == null || (below._overlay == this && _entries.Contains(below)), |
|
|
|
() => "The provided entry for `below` is not present in the Overlay."); |
|
|
|
D.assert(newEntriesList.All(entry => !_entries.Contains(entry)), |
|
|
|
() => "One or more of the specified entries are already present in the Overlay."); |
|
|
|
D.assert(newEntriesList.All(entry => entry._overlay == null), |
|
|
|
() => "One or more of the specified entries are already present in another Overlay.");*/ |
|
|
|
|
|
|
|
above == null || below == null, |
|
|
|
()=>"Only one of `above` and `below` may be specified." |
|
|
|
); |
|
|
|
above == null || below == null, |
|
|
|
() => "Only one of `above` and `below` may be specified." |
|
|
|
); |
|
|
|
above == null || (above._overlay == this && _entries.Contains(above) && newEntriesList.Contains(above)), |
|
|
|
()=>"The entry used for `above` must be in the Overlay and in the `newEntriesList`." |
|
|
|
); |
|
|
|
above == null || (above._overlay == this && _entries.Contains(above)), |
|
|
|
() => "The provided entry for `above` is not present in the Overlay." |
|
|
|
); |
|
|
|
below == null || (below._overlay == this && _entries.Contains(below) && newEntriesList.Contains(below)), |
|
|
|
()=>"The entry used for `below` must be in the Overlay and in the `newEntriesList`." |
|
|
|
); |
|
|
|
int overlayEntry = 0; |
|
|
|
foreach (var newEntry in newEntriesList) { |
|
|
|
if (newEntry._overlay == null || newEntry._overlay == this) { |
|
|
|
overlayEntry++; |
|
|
|
} |
|
|
|
} |
|
|
|
D.assert(overlayEntry == newEntriesList.Count, |
|
|
|
()=>"One or more of the specified entries are already present in another Overlay." |
|
|
|
); |
|
|
|
int lastoverlayEntry = 0; |
|
|
|
foreach (var newEntry in newEntriesList) { |
|
|
|
if (_entries.IndexOf(newEntry) == _entries.LastIndexOf(newEntry)) { |
|
|
|
lastoverlayEntry++; |
|
|
|
} |
|
|
|
} |
|
|
|
D.assert(lastoverlayEntry == newEntriesList.Count, |
|
|
|
()=>"One or more of the specified entries are specified multiple times." |
|
|
|
); |
|
|
|
below == null || (below._overlay == this && _entries.Contains(below)), |
|
|
|
() => "The provided entry for `below` is not present in the Overlay." |
|
|
|
); |
|
|
|
D.assert( |
|
|
|
newEntriesList.All(entry => entry._overlay == null || entry._overlay == this), |
|
|
|
() => "One or more of the specified entries are already present in another Overlay." |
|
|
|
); |
|
|
|
D.assert( |
|
|
|
newEntriesList.All(entry => _entries.IndexOf(entry) == _entries.LastIndexOf(entry)), |
|
|
|
() => "One or more of the specified entries are specified multiple times." |
|
|
|
); |
|
|
|
|
|
|
|
if (newEntriesList.isEmpty()) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
foreach (OverlayEntry entry in newEntriesList) { |
|
|
|
old.Remove(entry); |
|
|
|
} |
|
|
|
|
|
|
|
_entries.InsertRange(_insertionIndex(below, above), old); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
public override Widget build(BuildContext context) { |
|
|
|
var children = new List<Widget>(); |
|
|
|
int onstageCount = 0; |
|
|
|
int onstageCount = 0; |
|
|
|
for (var i = _entries.Count - 1; i >= 0; i -= 1) { |
|
|
|
var entry = _entries[i]; |
|
|
|
if (onstage) { |
|
|
|
|
|
|
return new _Theatre( |
|
|
|
skipCount: children.Count - onstageCount, |
|
|
|
children: children.ToList() |
|
|
|
// onstage: new Stack(
|
|
|
|
// fit: StackFit.expand,
|
|
|
|
// children: onstageChildren
|
|
|
|
// ),
|
|
|
|
// offstage: offstageChildren
|
|
|
|
|
|
|
|
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|
|
|
base.debugFillProperties(properties); |
|
|
|
|
|
|
|
properties.add(new DiagnosticsProperty<List<OverlayEntry>>("entries", _entries)); |
|
|
|
} |
|
|
|
List<Widget> children = null) : base(key, children) { |
|
|
|
D.assert(skipCount != null); |
|
|
|
List<Widget> children = null) : base(key: key, children: children) { |
|
|
|
D.assert(skipCount >= 0); |
|
|
|
D.assert(children != null); |
|
|
|
D.assert(children.Count() >= skipCount); |
|
|
|
|
|
|
public readonly int skipCount; |
|
|
|
|
|
|
|
public readonly Stack onstage; |
|
|
|
|
|
|
|
public readonly List<Widget> offstage; |
|
|
|
|
|
|
|
public override Element createElement() { |
|
|
|
return new _TheatreElement(this); |
|
|
|
} |
|
|
|
|
|
|
renderObject.skipCount = skipCount; |
|
|
|
renderObject.textDirection = Directionality.of(context); |
|
|
|
} |
|
|
|
|
|
|
|
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|
|
|
base.debugFillProperties(properties); |
|
|
|
|
|
|
|
properties.add(new IntProperty("skipCount", skipCount)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class _TheatreElement : MultiChildRenderObjectElement { |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Element _onstage;
|
|
|
|
// static readonly object _onstageSlot = new object();
|
|
|
|
//
|
|
|
|
// List<Element> _offstage;
|
|
|
|
// readonly HashSet<Element> _forgottenOffstageChildren = new HashSet<Element>();
|
|
|
|
//
|
|
|
|
// protected override void insertChildRenderObject(RenderObject child, object slot) {
|
|
|
|
// D.assert(this.renderObject.debugValidateChild(child));
|
|
|
|
// if (slot == _onstageSlot) {
|
|
|
|
// D.assert(child is RenderStack);
|
|
|
|
// this.renderObject.child = (RenderStack) child;
|
|
|
|
// }
|
|
|
|
// else {
|
|
|
|
// D.assert(slot == null || slot is Element);
|
|
|
|
// this.renderObject.insert((RenderBox) child, after: (RenderBox) ((Element) slot)?.renderObject);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// protected override void moveChildRenderObject(RenderObject child, object slot) {
|
|
|
|
// if (slot == _onstageSlot) {
|
|
|
|
// this.renderObject.remove((RenderBox) child);
|
|
|
|
// D.assert(child is RenderStack);
|
|
|
|
// this.renderObject.child = (RenderStack) child;
|
|
|
|
// }
|
|
|
|
// else {
|
|
|
|
// D.assert(slot == null || slot is Element);
|
|
|
|
// if (this.renderObject.child == child) {
|
|
|
|
// this.renderObject.child = null;
|
|
|
|
// this.renderObject.insert((RenderBox) child, after: (RenderBox) ((Element) slot)?.renderObject);
|
|
|
|
// }
|
|
|
|
// else {
|
|
|
|
// this.renderObject.move((RenderBox) child, after: (RenderBox) ((Element) slot)?.renderObject);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// protected override void removeChildRenderObject(RenderObject child) {
|
|
|
|
// if (this.renderObject.child == child) {
|
|
|
|
// this.renderObject.child = null;
|
|
|
|
// }
|
|
|
|
// else {
|
|
|
|
// this.renderObject.remove((RenderBox) child);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// public override void visitChildren(ElementVisitor visitor) {
|
|
|
|
// if (this._onstage != null) {
|
|
|
|
// visitor(this._onstage);
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// foreach (var child in this._offstage) {
|
|
|
|
// if (!this._forgottenOffstageChildren.Contains(child)) {
|
|
|
|
// visitor(child);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// // public override void debugVisitOnstageChildren(ElementVisitor visitor) {
|
|
|
|
// // if (this._onstage != null) {
|
|
|
|
// // visitor(this._onstage);
|
|
|
|
// // }
|
|
|
|
// // }
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// protected override void forgetChild(Element child) {
|
|
|
|
// if (child == this._onstage) {
|
|
|
|
// this._onstage = null;
|
|
|
|
// }
|
|
|
|
// else {
|
|
|
|
// D.assert(this._offstage.Contains(child));
|
|
|
|
// D.assert(!this._forgottenOffstageChildren.Contains(child));
|
|
|
|
// this._forgottenOffstageChildren.Add(child);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// public override void mount(Element parent, object newSlot) {
|
|
|
|
// base.mount(parent, newSlot);
|
|
|
|
// this._onstage = this.updateChild(this._onstage, this.widget.onstage, _onstageSlot);
|
|
|
|
// this._offstage = new List<Element>(this.widget.offstage.Count);
|
|
|
|
// Element previousChild = null;
|
|
|
|
// for (int i = 0; i < this._offstage.Count; i += 1) {
|
|
|
|
// var newChild = this.inflateWidget(this.widget.offstage[i], previousChild);
|
|
|
|
// this._offstage[i] = newChild;
|
|
|
|
// previousChild = newChild;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// public override void update(Widget newWidget) {
|
|
|
|
// base.update(newWidget);
|
|
|
|
// D.assert(Equals(this.widget, newWidget));
|
|
|
|
// this._onstage = this.updateChild(this._onstage, this.widget.onstage, _onstageSlot);
|
|
|
|
// this._offstage = this.updateChildren(this._offstage, this.widget.offstage,
|
|
|
|
// forgottenChildren: this._forgottenOffstageChildren);
|
|
|
|
// this._forgottenOffstageChildren.Clear();
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
class _RenderTheatre : |
|
|
|
ContainerRenderObjectMixinRenderProxyBoxMixinRenderObjectWithChildMixinRenderBoxRenderStack< |
|
|
|
RenderBox, StackParentData> { |
|
|
|
class _RenderTheatre : ContainerRenderObjectMixinRenderBox<RenderBox, StackParentData> { |
|
|
|
D.assert(skipCount != null); |
|
|
|
D.assert(textDirection != null); |
|
|
|
_textDirection = textDirection; |
|
|
|
_skipCount = skipCount; |
|
|
|
addAll(children); |
|
|
|
|
|
|
|
|
|
|
public override void setupParentData(RenderObject child) { |
|
|
|
if (!(child.parentData is StackParentData)) { |
|
|
|
child.parentData = new StackParentData(); |
|
|
|
} |
|
|
|
} |
|
|
|
if (_resolvedAlignment != null) |
|
|
|
if (_resolvedAlignment != null) { |
|
|
|
// TODO: AlignmentDirectional
|
|
|
|
Alignment al = Alignment.topLeft; |
|
|
|
switch (textDirection) { |
|
|
|
} |
|
|
|
//FIXME: wait for changes on painting/alignment.cs (by Siyao)
|
|
|
|
//if you wanna to skip this change, just comment out the line below and use the commented lines after it
|
|
|
|
_resolvedAlignment = AlignmentDirectional.topStart.resolve(textDirection); |
|
|
|
/*switch (textDirection) { |
|
|
|
case TextDirection.rtl: |
|
|
|
_resolvedAlignment = new Alignment(-1, -1); |
|
|
|
break; |
|
|
|
|
|
|
} |
|
|
|
}*/ |
|
|
|
} |
|
|
|
|
|
|
|
void _markNeedResolution() { |
|
|
|
|
|
|
public int skipCount { |
|
|
|
get { return _skipCount; } |
|
|
|
set { |
|
|
|
D.assert(value != null); |
|
|
|
if (_skipCount != value) { |
|
|
|
_skipCount = value; |
|
|
|
markNeedsLayout(); |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public override void setupParentData(RenderObject child) { |
|
|
|
if (!(child.parentData is StackParentData)) { |
|
|
|
child.parentData = new StackParentData(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public override void redepthChildren() { |
|
|
|
if (child != null) { |
|
|
|
redepthChild(child); |
|
|
|
} |
|
|
|
|
|
|
|
base.redepthChildren(); |
|
|
|
} |
|
|
|
|
|
|
|
public override void visitChildren(RenderObjectVisitor visitor) { |
|
|
|
if (child != null) { |
|
|
|
visitor(child); |
|
|
|
} |
|
|
|
|
|
|
|
base.visitChildren(visitor); |
|
|
|
} |
|
|
|
|
|
|
|
public override List<DiagnosticsNode> debugDescribeChildren() { |
|
|
|
var children = new List<DiagnosticsNode>(); |
|
|
|
|
|
|
|
if (this.child != null) { |
|
|
|
children.Add(child.toDiagnosticsNode(name: "onstage")); |
|
|
|
} |
|
|
|
|
|
|
|
if (firstChild != null) { |
|
|
|
var child = firstChild; |
|
|
|
|
|
|
|
int count = 1; |
|
|
|
while (true) { |
|
|
|
children.Add( |
|
|
|
child.toDiagnosticsNode( |
|
|
|
name: $"offstage {count}", |
|
|
|
style: DiagnosticsTreeStyle.offstage |
|
|
|
) |
|
|
|
); |
|
|
|
if (child == lastChild) { |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
var childParentData = (StackParentData) child.parentData; |
|
|
|
child = childParentData.nextSibling; |
|
|
|
count += 1; |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
children.Add( |
|
|
|
DiagnosticsNode.message( |
|
|
|
"no offstage children", |
|
|
|
style: DiagnosticsTreeStyle.offstage |
|
|
|
) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
return children; |
|
|
|
} |
|
|
|
|
|
|
|
RenderBox _lastOnstageChild { |
|
|
|
get { return skipCount == childCount ? null : lastChild; } |
|
|
|
} |
|
|
|
|
|
|
if (candidate != null) { |
|
|
|
candidate += childParentData.offset.dy; |
|
|
|
if (result != null) { |
|
|
|
result = Math.Min(result.Value, candidate.Value); |
|
|
|
result = Mathf.Min(result.Value, candidate.Value); |
|
|
|
} |
|
|
|
else { |
|
|
|
result = candidate; |
|
|
|
|
|
|
|
|
|
|
_resolve(); |
|
|
|
D.assert(_resolvedAlignment != null); |
|
|
|
|
|
|
|
// Same BoxConstraints as used by RenderStack for StackFit.expand.
|
|
|
|
|
|
|
|
BoxConstraints nonPositionedConstraints = BoxConstraints.tight(constraints.biggest); |
|
|
|
|
|
|
|
RenderBox child = _firstOnstageChild; |
|
|
|
|
|
|
for (int i = 0; i < _onstageChildCount; i++) { |
|
|
|
D.assert(child != null); |
|
|
|
StackParentData childParentData = child.parentData as StackParentData; |
|
|
|
|
|
|
|
if (childParentData.offset.dx != 0 || childParentData.offset.dy != 0) { |
|
|
|
i = i; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool isHit = result.addWithPaintOffset( |
|
|
|
offset: childParentData.offset, |
|
|
|
position: position, |
|
|
|
|
|
|
} |
|
|
|
else { |
|
|
|
paintStack(context, offset); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void visitChildrenForSemantics(RenderObjectVisitor visitor) { |
|
|
|
RenderBox child = _firstOnstageChild; |
|
|
|
while (child != null) { |
|
|
|
visitor(child); |
|
|
|
StackParentData childParentData = child.parentData as StackParentData; |
|
|
|
child = childParentData.nextSibling; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
base.debugFillProperties(properties); |
|
|
|
properties.add(new IntProperty("skipCount", skipCount)); |
|
|
|
properties.add(new EnumProperty<TextDirection>("textDirection", textDirection)); |
|
|
|
} |
|
|
|
|
|
|
|
public override List<DiagnosticsNode> debugDescribeChildren() { |
|
|
|
List<DiagnosticsNode> offstageChildren = new List<DiagnosticsNode>(); |
|
|
|
List<DiagnosticsNode> onstageChildren = new List<DiagnosticsNode>(); |
|
|
|
|
|
|
|
int count = 1; |
|
|
|
bool onstage = false; |
|
|
|
RenderBox child = firstChild; |
|
|
|
RenderBox firstOnstageChild = _firstOnstageChild; |
|
|
|
while (child != null) { |
|
|
|
if (child == firstOnstageChild) { |
|
|
|
onstage = true; |
|
|
|
count = 1; |
|
|
|
} |
|
|
|
|
|
|
|
if (onstage) { |
|
|
|
onstageChildren.Add( |
|
|
|
child.toDiagnosticsNode( |
|
|
|
name: $"onstage {count}" |
|
|
|
) |
|
|
|
); |
|
|
|
} else { |
|
|
|
offstageChildren.Add( |
|
|
|
child.toDiagnosticsNode( |
|
|
|
name: $"offstage {count}", |
|
|
|
style: DiagnosticsTreeStyle.offstage |
|
|
|
) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
StackParentData childParentData = child.parentData as StackParentData; |
|
|
|
child = childParentData.nextSibling; |
|
|
|
count += 1; |
|
|
|
} |
|
|
|
|
|
|
|
List<DiagnosticsNode> result = new List<DiagnosticsNode>(); |
|
|
|
result.AddRange(onstageChildren); |
|
|
|
if (offstageChildren.isNotEmpty()) { |
|
|
|
result.AddRange(offstageChildren); |
|
|
|
} |
|
|
|
else { |
|
|
|
result.Add(DiagnosticsNode.message("no offstage children", style: DiagnosticsTreeStyle.offstage)); |
|
|
|
} |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
} |