浏览代码
Merge branch 'master' into 'master'
Merge branch 'master' into 'master'
navigation bar & custom paint See merge request upm-packages/ui-widgets/com.unity.uiwidgets!20/main
Shenhua Gu
6 年前
当前提交
abd4640f
共有 12 个文件被更改,包括 942 次插入 和 24 次删除
-
1Runtime/widgets/app.cs
-
104Runtime/widgets/basic.cs
-
73Samples/UIWidgetSample/Navigation.unity
-
85Samples/UIWidgetSample/NavigationCanvas.cs
-
238Runtime/rendering/custom_layout.cs
-
11Runtime/rendering/custom_layout.cs.meta
-
194Runtime/widgets/custom_paint.cs
-
11Runtime/widgets/custom_paint.cs.meta
-
158Runtime/widgets/navigation_toolbar.cs
-
11Runtime/widgets/navigation_toolbar.cs.meta
-
69Samples/UIWidgetSample/CustomPaintCanvas.cs
-
11Samples/UIWidgetSample/CustomPaintCanvas.cs.meta
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.ui; |
|||
|
|||
namespace Unity.UIWidgets.rendering { |
|||
public class MultiChildLayoutParentData : ContainerParentDataMixinBoxParentData<RenderBox> { |
|||
public object id; |
|||
|
|||
public override string ToString() { |
|||
return $"{base.ToString()}; id={this.id}"; |
|||
} |
|||
} |
|||
|
|||
public abstract class MultiChildLayoutDelegate { |
|||
Dictionary<object, RenderBox> _idToChild; |
|||
HashSet<RenderBox> _debugChildrenNeedingLayout; |
|||
|
|||
public bool hasChild(object childId) { |
|||
return this._idToChild.getOrDefault(childId) != null; |
|||
} |
|||
|
|||
public Size layoutChild(object childId, BoxConstraints constraints) { |
|||
RenderBox child = this._idToChild[childId]; |
|||
D.assert(() => { |
|||
if (child == null) |
|||
throw new UIWidgetsError( |
|||
$"The {this} custom multichild layout delegate tried to lay out a non-existent child.\n" + |
|||
$"There is no child with the id \"{childId}\"." |
|||
); |
|||
|
|||
if (!this._debugChildrenNeedingLayout.Remove(child)) |
|||
throw new UIWidgetsError( |
|||
$"The $this custom multichild layout delegate tried to lay out the child with id \"{childId}\" more than once.\n" + |
|||
"Each child must be laid out exactly once." |
|||
); |
|||
|
|||
try { |
|||
D.assert(constraints.debugAssertIsValid(isAppliedConstraint: true)); |
|||
} |
|||
catch (AssertionError exception) { |
|||
throw new UIWidgetsError( |
|||
$"The {this} custom multichild layout delegate provided invalid box constraints for the child with id \"{childId}\".\n" + |
|||
$"{exception}n" + |
|||
"The minimum width and height must be greater than or equal to zero.\n" + |
|||
"The maximum width must be greater than or equal to the minimum width.\n" + |
|||
"The maximum height must be greater than or equal to the minimum height."); |
|||
} |
|||
|
|||
return true; |
|||
}); |
|||
child.layout(constraints, parentUsesSize: true); |
|||
return child.size; |
|||
} |
|||
|
|||
public void positionChild(object childId, Offset offset) { |
|||
RenderBox child = this._idToChild[childId]; |
|||
D.assert(() => { |
|||
if (child == null) |
|||
throw new UIWidgetsError( |
|||
$"The {this} custom multichild layout delegate tried to position out a non-existent child:\n" + |
|||
$"There is no child with the id \"{childId}\"." |
|||
); |
|||
|
|||
if (offset == null) |
|||
throw new UIWidgetsError( |
|||
$"The {this} custom multichild layout delegate provided a null position for the child with id \"{childId}\"." |
|||
); |
|||
|
|||
return true; |
|||
}); |
|||
MultiChildLayoutParentData childParentData = (MultiChildLayoutParentData) child.parentData; |
|||
childParentData.offset = offset; |
|||
} |
|||
|
|||
string _debugDescribeChild(RenderBox child) { |
|||
MultiChildLayoutParentData childParentData = (MultiChildLayoutParentData) child.parentData; |
|||
return $"{childParentData.id}: {child}"; |
|||
} |
|||
|
|||
|
|||
internal void _callPerformLayout(Size size, RenderBox firstChild) { |
|||
Dictionary<object, RenderBox> previousIdToChild = this._idToChild; |
|||
|
|||
HashSet<RenderBox> debugPreviousChildrenNeedingLayout = null; |
|||
D.assert(() => { |
|||
debugPreviousChildrenNeedingLayout = this._debugChildrenNeedingLayout; |
|||
this._debugChildrenNeedingLayout = new HashSet<RenderBox>(); |
|||
return true; |
|||
}); |
|||
|
|||
try { |
|||
this._idToChild = new Dictionary<object, RenderBox>(); |
|||
RenderBox child = firstChild; |
|||
while (child != null) { |
|||
MultiChildLayoutParentData childParentData = (MultiChildLayoutParentData) child.parentData; |
|||
D.assert(() => { |
|||
if (childParentData.id == null) |
|||
throw new UIWidgetsError( |
|||
"The following child has no ID:\n" + |
|||
$" {child}\n" + |
|||
"Every child of a RenderCustomMultiChildLayoutBox must have an ID in its parent data." |
|||
); |
|||
|
|||
return true; |
|||
}); |
|||
this._idToChild[childParentData.id] = child; |
|||
D.assert(() => { |
|||
this._debugChildrenNeedingLayout.Add(child); |
|||
return true; |
|||
}); |
|||
child = childParentData.nextSibling; |
|||
} |
|||
|
|||
this.performLayout(size); |
|||
D.assert(() => { |
|||
if (this._debugChildrenNeedingLayout.isNotEmpty()) { |
|||
if (this._debugChildrenNeedingLayout.Count > 1) |
|||
throw new UIWidgetsError( |
|||
$"The $this custom multichild layout delegate forgot to lay out the following children:\n" + |
|||
$" {string.Join("\n ", this._debugChildrenNeedingLayout.Select(this._debugDescribeChild))}\n" + |
|||
"Each child must be laid out exactly once." |
|||
); |
|||
else |
|||
throw new UIWidgetsError( |
|||
$"The $this custom multichild layout delegate forgot to lay out the following child:\n" + |
|||
$" {this._debugDescribeChild(this._debugChildrenNeedingLayout.First())}\n" + |
|||
"Each child must be laid out exactly once." |
|||
); |
|||
} |
|||
|
|||
return true; |
|||
}); |
|||
} |
|||
finally { |
|||
this._idToChild = previousIdToChild; |
|||
D.assert(() => { |
|||
this._debugChildrenNeedingLayout = debugPreviousChildrenNeedingLayout; |
|||
return true; |
|||
}); |
|||
} |
|||
} |
|||
|
|||
public virtual Size getSize(BoxConstraints constraints) { |
|||
return constraints.biggest; |
|||
} |
|||
|
|||
|
|||
public abstract void performLayout(Size size); |
|||
|
|||
|
|||
public abstract bool shouldRelayout(MultiChildLayoutDelegate oldDelegate); |
|||
|
|||
public override string ToString() { |
|||
return $"{this.GetType()}"; |
|||
} |
|||
} |
|||
|
|||
public class RenderCustomMultiChildLayoutBox : RenderBoxContainerDefaultsMixinContainerRenderObjectMixinRenderBox< |
|||
RenderBox |
|||
, MultiChildLayoutParentData> { |
|||
public RenderCustomMultiChildLayoutBox( |
|||
List<RenderBox> children = null, |
|||
MultiChildLayoutDelegate layoutDelegate = null |
|||
) { |
|||
D.assert(layoutDelegate != null); |
|||
this._delegate = layoutDelegate; |
|||
this.addAll(children); |
|||
} |
|||
|
|||
public override void setupParentData(RenderObject child) { |
|||
if (!(child.parentData is MultiChildLayoutParentData)) |
|||
child.parentData = new MultiChildLayoutParentData(); |
|||
} |
|||
|
|||
public MultiChildLayoutDelegate layoutDelegate { |
|||
get => this._delegate; |
|||
set { |
|||
D.assert(value != null); |
|||
if (this._delegate == value) |
|||
return; |
|||
if (value.GetType() != this._delegate.GetType() || value.shouldRelayout(this._delegate)) |
|||
this.markNeedsLayout(); |
|||
this._delegate = value; |
|||
} |
|||
} |
|||
|
|||
MultiChildLayoutDelegate _delegate; |
|||
|
|||
|
|||
Size _getSize(BoxConstraints constraints) { |
|||
D.assert(constraints.debugAssertIsValid()); |
|||
return constraints.constrain(this._delegate.getSize(constraints)); |
|||
} |
|||
|
|||
protected override double computeMinIntrinsicWidth(double height) { |
|||
double width = this._getSize(BoxConstraints.tightForFinite(height: height)).width; |
|||
if (width.isFinite()) |
|||
return width; |
|||
return 0.0; |
|||
} |
|||
|
|||
protected override double computeMaxIntrinsicWidth(double height) { |
|||
double width = this._getSize(BoxConstraints.tightForFinite(height: height)).width; |
|||
if (width.isFinite()) |
|||
return width; |
|||
return 0.0; |
|||
} |
|||
|
|||
protected override double computeMinIntrinsicHeight(double width) { |
|||
double height = this._getSize(BoxConstraints.tightForFinite(width: width)).height; |
|||
if (height.isFinite()) |
|||
return height; |
|||
return 0.0; |
|||
} |
|||
|
|||
protected override double computeMaxIntrinsicHeight(double width) { |
|||
double height = this._getSize(BoxConstraints.tightForFinite(width: width)).height; |
|||
if (height.isFinite()) |
|||
return height; |
|||
return 0.0; |
|||
} |
|||
|
|||
protected override void performLayout() { |
|||
this.size = this._getSize(this.constraints); |
|||
this.layoutDelegate._callPerformLayout(this.size, this.firstChild); |
|||
} |
|||
|
|||
public override void paint(PaintingContext context, Offset offset) { |
|||
this.defaultPaint(context, offset); |
|||
} |
|||
|
|||
protected override bool hitTestChildren(HitTestResult result, Offset position) { |
|||
return this.defaultHitTestChildren(result, position: position); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b5b9bc11eb396425ab97e383d769356c |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
|
|||
namespace Unity.UIWidgets.widgets { |
|||
public abstract class CustomPainter : Listenable { |
|||
public CustomPainter(Listenable repaint) { |
|||
this._repaint = repaint; |
|||
} |
|||
|
|||
readonly Listenable _repaint; |
|||
|
|||
public void addListener(VoidCallback listener) { |
|||
this._repaint?.addListener(listener); |
|||
} |
|||
|
|||
public void removeListener(VoidCallback listener) { |
|||
this._repaint?.removeListener(listener); |
|||
} |
|||
|
|||
public abstract void paint(Canvas canvas, Size size); |
|||
|
|||
public abstract bool shouldRepaint(CustomPainter oldDelegate); |
|||
|
|||
public virtual bool hitTest(Offset position) { |
|||
return false; |
|||
} |
|||
|
|||
public override string ToString() { |
|||
return $"{Diagnostics.describeIdentity(this)}({this._repaint?.ToString() ?? ""})"; |
|||
} |
|||
} |
|||
|
|||
public class RenderCustomPaint : RenderProxyBox { |
|||
|
|||
public RenderCustomPaint( |
|||
CustomPainter painter = null, |
|||
CustomPainter foregroundPainter = null, |
|||
Size preferredSize = null, |
|||
bool isComplex = false, |
|||
bool willChange = false, |
|||
RenderBox child = null |
|||
): base(child) { |
|||
preferredSize = preferredSize ?? Size.zero; |
|||
this.preferredSize = preferredSize; |
|||
this._painter = painter; |
|||
this._foregroundPainter = foregroundPainter; |
|||
this.isComplex = isComplex; |
|||
this.willChange = willChange; |
|||
} |
|||
|
|||
CustomPainter _painter; |
|||
|
|||
public CustomPainter painter { |
|||
get => this._painter; |
|||
set { |
|||
if (this._painter == value) |
|||
return; |
|||
CustomPainter oldPainter = this._painter; |
|||
this._painter = value; |
|||
this._didUpdatePainter(this._painter, oldPainter); |
|||
} |
|||
} |
|||
|
|||
CustomPainter _foregroundPainter; |
|||
|
|||
public CustomPainter foregroundPainter { |
|||
get => this._foregroundPainter; |
|||
set { |
|||
if (this._foregroundPainter == value) |
|||
return; |
|||
CustomPainter oldPainter = this._foregroundPainter; |
|||
this._foregroundPainter = value; |
|||
this._didUpdatePainter(this._foregroundPainter, oldPainter); |
|||
} |
|||
} |
|||
|
|||
void _didUpdatePainter(CustomPainter newPainter, CustomPainter oldPainter) { |
|||
if (newPainter == null) { |
|||
D.assert(oldPainter != null); |
|||
this.markNeedsPaint(); |
|||
} |
|||
else if (oldPainter == null || |
|||
newPainter.GetType() != oldPainter.GetType() || |
|||
newPainter.shouldRepaint(oldPainter)) { |
|||
this.markNeedsPaint(); |
|||
} |
|||
|
|||
if (this.attached) { |
|||
oldPainter?.removeListener(this.markNeedsPaint); |
|||
newPainter?.addListener(this.markNeedsPaint); |
|||
} |
|||
} |
|||
|
|||
Size _preferredSize; |
|||
|
|||
public Size preferredSize { |
|||
get => this._preferredSize; |
|||
set { |
|||
D.assert(value != null); |
|||
if (this.preferredSize == value) |
|||
return; |
|||
this._preferredSize = value; |
|||
this.markNeedsLayout(); |
|||
} |
|||
} |
|||
|
|||
public bool isComplex; |
|||
|
|||
public bool willChange; |
|||
|
|||
public override void attach(object owner) { |
|||
base.attach(owner); |
|||
this._painter?.addListener(this.markNeedsPaint); |
|||
this._foregroundPainter?.addListener(this.markNeedsPaint); |
|||
} |
|||
|
|||
public override void detach() { |
|||
this._painter?.removeListener(this.markNeedsPaint); |
|||
this._foregroundPainter?.removeListener(this.markNeedsPaint); |
|||
base.detach(); |
|||
} |
|||
|
|||
protected override bool hitTestChildren(HitTestResult result, Offset position) { |
|||
if (this._foregroundPainter != null && (this._foregroundPainter.hitTest(position))) |
|||
return true; |
|||
return base.hitTestChildren(result, position: position); |
|||
} |
|||
|
|||
|
|||
protected override bool hitTestSelf(Offset position) { |
|||
return this._painter != null && this._painter.hitTest(position); |
|||
} |
|||
|
|||
protected override void performResize() { |
|||
this.size = this.constraints.constrain(this.preferredSize); |
|||
} |
|||
|
|||
void _paintWithPainter(Canvas canvas, Offset offset, CustomPainter painter) { |
|||
int debugPreviousCanvasSaveCount = 0; |
|||
canvas.save(); |
|||
D.assert(() => { |
|||
debugPreviousCanvasSaveCount = canvas.getSaveCount(); |
|||
return true; |
|||
}); |
|||
if (offset != Offset.zero) |
|||
canvas.translate(offset.dx, offset.dy); |
|||
painter.paint(canvas, this.size); |
|||
D.assert(() => { |
|||
int debugNewCanvasSaveCount = canvas.getSaveCount(); |
|||
if (debugNewCanvasSaveCount > debugPreviousCanvasSaveCount) |
|||
throw new UIWidgetsError( |
|||
$"{debugNewCanvasSaveCount - debugPreviousCanvasSaveCount} more " + |
|||
$"time{((debugNewCanvasSaveCount - debugPreviousCanvasSaveCount == 1) ? "" : "s")} " + |
|||
"than it called canvas.restore().\n" + |
|||
"This leaves the canvas in an inconsistent state and will probably result in a broken display.\n" + |
|||
"You must pair each call to save()/saveLayer() with a later matching call to restore()." |
|||
); |
|||
if (debugNewCanvasSaveCount < debugPreviousCanvasSaveCount) |
|||
throw new UIWidgetsError( |
|||
$"The {painter} custom painter called canvas.restore() " + |
|||
$"{debugPreviousCanvasSaveCount - debugNewCanvasSaveCount} more " + |
|||
$"time{(debugPreviousCanvasSaveCount - debugNewCanvasSaveCount == 1 ? "" : "s")} " + |
|||
"than it called canvas.save() or canvas.saveLayer().\n" + |
|||
"This leaves the canvas in an inconsistent state and will result in a broken display.\n" + |
|||
"You should only call restore() if you first called save() or saveLayer()." |
|||
); |
|||
return debugNewCanvasSaveCount == debugPreviousCanvasSaveCount; |
|||
}); |
|||
canvas.restore(); |
|||
} |
|||
|
|||
public override void paint(PaintingContext context, Offset offset) { |
|||
if (this._painter != null) { |
|||
this._paintWithPainter(context.canvas, offset, this._painter); |
|||
this._setRasterCacheHints(context); |
|||
} |
|||
|
|||
base.paint(context, offset); |
|||
if (this._foregroundPainter != null) { |
|||
this._paintWithPainter(context.canvas, offset, this._foregroundPainter); |
|||
this._setRasterCacheHints(context); |
|||
} |
|||
} |
|||
|
|||
void _setRasterCacheHints(PaintingContext context) { |
|||
if (this.isComplex) |
|||
context.setIsComplexHint(); |
|||
if (this.willChange) |
|||
context.setWillChangeHint(); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: a4b3c34c9775f4b9995fb37535490751 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
|
|||
namespace Unity.UIWidgets.widgets { |
|||
public class NavigationToolbar : StatelessWidget { |
|||
public NavigationToolbar(Key key = null, Widget leading = null, Widget middle = null, |
|||
Widget trailing = null, bool centerMiddle = true, double middleSpacing = kMiddleSpacing) : base(key) { |
|||
this.leading = leading; |
|||
this.middle = middle; |
|||
this.trailing = trailing; |
|||
this.centerMiddle = centerMiddle; |
|||
this.middleSpacing = middleSpacing; |
|||
} |
|||
|
|||
public const double kMiddleSpacing = 16.0; |
|||
|
|||
public readonly Widget leading; |
|||
public readonly Widget middle; |
|||
public readonly Widget trailing; |
|||
public readonly bool centerMiddle; |
|||
public readonly double middleSpacing; |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
D.assert(WidgetsD.debugCheckHasDirectionality(context)); |
|||
List<Widget> children = new List<Widget>(); |
|||
|
|||
if (this.leading != null) |
|||
children.Add(new LayoutId(id: _ToolbarSlot.leading, child: this.leading)); |
|||
|
|||
if (this.middle != null) |
|||
children.Add(new LayoutId(id: _ToolbarSlot.middle, child: this.middle)); |
|||
|
|||
if (this.trailing != null) |
|||
children.Add(new LayoutId(id: _ToolbarSlot.trailing, child: this.trailing)); |
|||
|
|||
TextDirection textDirection = Directionality.of(context); |
|||
return new CustomMultiChildLayout( |
|||
layoutDelegate: new _ToolbarLayout( |
|||
centerMiddle: this.centerMiddle, |
|||
middleSpacing: this.middleSpacing, |
|||
textDirection: textDirection |
|||
), |
|||
children: children |
|||
); |
|||
} |
|||
} |
|||
|
|||
internal enum _ToolbarSlot { |
|||
leading, |
|||
middle, |
|||
trailing, |
|||
} |
|||
|
|||
internal class _ToolbarLayout : MultiChildLayoutDelegate { |
|||
public _ToolbarLayout( |
|||
bool? centerMiddle = true, |
|||
double? middleSpacing = null, |
|||
TextDirection? textDirection = null |
|||
) { |
|||
D.assert(textDirection != null); |
|||
D.assert(middleSpacing != null); |
|||
this.centerMiddle = centerMiddle??true; |
|||
this.middleSpacing = middleSpacing ?? 0.0; |
|||
this.textDirection = textDirection ?? TextDirection.ltr; |
|||
} |
|||
|
|||
|
|||
public readonly bool centerMiddle; |
|||
|
|||
public readonly double middleSpacing; |
|||
|
|||
public readonly TextDirection textDirection; |
|||
|
|||
public override void performLayout(Size size) { |
|||
double leadingWidth = 0.0; |
|||
double trailingWidth = 0.0; |
|||
|
|||
if (this.hasChild(_ToolbarSlot.leading)) { |
|||
BoxConstraints constraints = new BoxConstraints( |
|||
minWidth: 0.0, |
|||
maxWidth: size.width / 3.0, |
|||
minHeight: size.height, |
|||
maxHeight: size.height |
|||
); |
|||
leadingWidth = this.layoutChild(_ToolbarSlot.leading, constraints).width; |
|||
double leadingX = 0.0; |
|||
switch (this.textDirection) { |
|||
case TextDirection.rtl: |
|||
leadingX = size.width - leadingWidth; |
|||
break; |
|||
case TextDirection.ltr: |
|||
leadingX = 0.0; |
|||
break; |
|||
} |
|||
|
|||
this.positionChild(_ToolbarSlot.leading, new Offset(leadingX, 0.0)); |
|||
} |
|||
|
|||
if (this.hasChild(_ToolbarSlot.trailing)) { |
|||
BoxConstraints constraints = BoxConstraints.loose(size); |
|||
Size trailingSize = this.layoutChild(_ToolbarSlot.trailing, constraints); |
|||
double trailingX = 0.0; |
|||
switch (this.textDirection) { |
|||
case TextDirection.rtl: |
|||
trailingX = 0.0; |
|||
break; |
|||
case TextDirection.ltr: |
|||
trailingX = size.width - trailingSize.width; |
|||
break; |
|||
} |
|||
|
|||
double trailingY = (size.height - trailingSize.height) / 2.0; |
|||
trailingWidth = trailingSize.width; |
|||
this.positionChild(_ToolbarSlot.trailing, new Offset(trailingX, trailingY)); |
|||
} |
|||
|
|||
if (this.hasChild(_ToolbarSlot.middle)) { |
|||
double maxWidth = Math.Max(size.width - leadingWidth - trailingWidth - this.middleSpacing * 2.0, 0.0); |
|||
BoxConstraints constraints = BoxConstraints.loose(size).copyWith(maxWidth: maxWidth); |
|||
Size middleSize = this.layoutChild(_ToolbarSlot.middle, constraints); |
|||
|
|||
double middleStartMargin = leadingWidth + this.middleSpacing; |
|||
double middleStart = middleStartMargin; |
|||
double middleY = (size.height - middleSize.height) / 2.0; |
|||
// If the centered middle will not fit between the leading and trailing
|
|||
// widgets, then align its left or right edge with the adjacent boundary.
|
|||
if (this.centerMiddle) { |
|||
middleStart = (size.width - middleSize.width) / 2.0; |
|||
if (middleStart + middleSize.width > size.width - trailingWidth) |
|||
middleStart = size.width - trailingWidth - middleSize.width; |
|||
else if (middleStart < middleStartMargin) |
|||
middleStart = middleStartMargin; |
|||
} |
|||
|
|||
double middleX = 0.0; |
|||
switch (this.textDirection) { |
|||
case TextDirection.rtl: |
|||
middleX = size.width - middleSize.width - middleStart; |
|||
break; |
|||
case TextDirection.ltr: |
|||
middleX = middleStart; |
|||
break; |
|||
} |
|||
|
|||
this.positionChild(_ToolbarSlot.middle, new Offset(middleX, middleY)); |
|||
} |
|||
} |
|||
|
|||
public override bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) { |
|||
return ((_ToolbarLayout) oldDelegate).centerMiddle != this.centerMiddle |
|||
|| ((_ToolbarLayout) oldDelegate).middleSpacing != this.middleSpacing |
|||
|| ((_ToolbarLayout) oldDelegate).textDirection != this.textDirection; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 0d3291a5dbe68445896df6360fda57c8 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using Unity.UIWidgets.engine; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
|
|||
namespace UIWidgetsSample { |
|||
|
|||
public class CustomPaintCanvas: WidgetCanvas { |
|||
|
|||
protected override Widget getWidget() { |
|||
return new CustomPaint( |
|||
child: new Container(width: 300, height: 300, color: new Color(0XFFFFFFFF)), |
|||
foregroundPainter: new GridPainter(null) |
|||
); |
|||
} |
|||
} |
|||
|
|||
public class GridPainter : CustomPainter { |
|||
public GridPainter(Listenable repaint) : base(repaint) { |
|||
} |
|||
|
|||
public override void paint(Canvas canvas, Size size) { |
|||
int numGrid = 4; |
|||
var paint = new Paint(); |
|||
paint.color = new Color(0xFFFF0000); |
|||
paint.strokeWidth = 2; |
|||
paint.style = PaintingStyle.stroke; |
|||
for (int i = 1; i < numGrid; i++) { |
|||
double offsetY = size.height * i / numGrid; |
|||
canvas.drawLine(new Offset(0, offsetY), new Offset(size.width, offsetY), |
|||
paint); |
|||
} |
|||
for (int i = 1; i < numGrid; i++) { |
|||
double offsetx = size.width * i / numGrid; |
|||
canvas.drawLine(new Offset(offsetx, 0), new Offset(offsetx, size.height), |
|||
paint); |
|||
} |
|||
|
|||
|
|||
// draw a arrow line
|
|||
canvas.save(); |
|||
canvas.rotate(0.4); |
|||
canvas.scale(2, 2); |
|||
canvas.translate(50, 50); |
|||
canvas.drawLine(new Offset(0, 0), new Offset(100, 0), |
|||
new Paint(){ |
|||
color = new Color(0xFFFF0000), |
|||
strokeWidth = 2, |
|||
style = PaintingStyle.stroke |
|||
}); |
|||
var path = new Path(); |
|||
var arrowPaint = new Paint() { |
|||
color = new Color(0xFFFF0000), |
|||
style = PaintingStyle.fill |
|||
}; |
|||
path.moveTo(100, 0); |
|||
path.lineTo(100, 5); |
|||
path.lineTo(120, 0); |
|||
path.lineTo(100, -5); |
|||
path.close(); |
|||
canvas.drawPath(path, arrowPaint); |
|||
canvas.restore(); |
|||
} |
|||
|
|||
public override bool shouldRepaint(CustomPainter oldDelegate) { |
|||
return false; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 5debfd6de8ea942d487383a56aad8491 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue