diff --git a/Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/FlowSample.cs b/Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/FlowSample.cs new file mode 100644 index 00000000..af8a219d --- /dev/null +++ b/Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/FlowSample.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using uiwidgets; +using Unity.UIWidgets.animation; +using Unity.UIWidgets.engine; +using Unity.UIWidgets.foundation; +using Unity.UIWidgets.material; +using Unity.UIWidgets.painting; +using Unity.UIWidgets.rendering; +using Unity.UIWidgets.ui; +using Unity.UIWidgets.widgets; +using ui_ = Unity.UIWidgets.widgets.ui_; + +namespace UIWidgetsSample +{ + public class FlowSample : UIWidgetsPanel + { + protected override void main() + { + ui_.runApp(new MaterialApp( + showPerformanceOverlay: false, + home: new FlowDemo())); + } + + protected new void OnEnable() + { + base.OnEnable(); + } + + protected override void onEnable() + { + AddFont("Material Icons", new List {"MaterialIcons-Regular.ttf"}, new List {0}); + } + } + + public class FlowDemo : StatefulWidget + { + public FlowDemo(Key key = null) : base(key) + { + } + + public override State createState() + { + return new _FlowDemoState(); + } + } + + class FlowDemoDelegate : FlowDelegate + { + public FlowDemoDelegate(Animation myAnimation) : base(repaint: myAnimation) + { + this.myAnimation = myAnimation; + } + + Animation myAnimation; + + public override bool shouldRepaint(FlowDelegate oldDelegate) + { + var _oldDelegate = oldDelegate as FlowDemoDelegate; + return myAnimation != _oldDelegate?.myAnimation; + } + + public override void paintChildren(FlowPaintingContext context) + { + for (int i = context.childCount - 1; i >= 0; i--) + { + float dx = (context.getChildSize(i).height + 10) * i; + context.paintChild( + i, + transform: Matrix4.translationValues(0, dx * myAnimation.value + 10, 0) + ); + } + } + + public override Size getSize(BoxConstraints constraints) + { + return new Size(70.0f, float.PositiveInfinity); + } + + + public override BoxConstraints getConstraintsForChild(int i, BoxConstraints constraints) + { + return i == 0 ? constraints : BoxConstraints.tight(new Size(50.0f, 50.0f)); + } + } + + public class _FlowDemoState : SingleTickerProviderStateMixin + { + private AnimationController _myAnimation; + + List _icons = new List + { + Icons.menu, + Icons.email, + Icons.settings, + Icons.notifications, + Icons.person, + Icons.wifi + }; + + public override void initState() + { + base.initState(); + + _myAnimation = new AnimationController( + duration: new TimeSpan(0, 0, 0, 1), + vsync: this + ); + } + + Widget _buildItem(IconData icon) + { + return new Padding( + padding: EdgeInsets.symmetric(horizontal: 10.0f), + child: new RawMaterialButton( + fillColor: Colors.cyan, + shape: new CircleBorder(), + constraints: BoxConstraints.tight(Size.square(50.0f)), + onPressed: () => + { + if (_myAnimation.status == AnimationStatus.completed) + { + _myAnimation.reverse(); + } + else + { + _myAnimation.forward(); + } + }, + child: new Icon( + icon, + color: Colors.white, + size: 30.0f + ) + ) + ); + } + + public override Widget build(BuildContext context) + { + return new Scaffold( + appBar: new AppBar( + automaticallyImplyLeading: false, + title: new Text("Flutter Flow Widget Demo"), + backgroundColor: Colors.cyan + ), + body: new Stack( + children: new List + { + new Container(color: Colors.grey[200]), + new Flow( + _delegate: new FlowDemoDelegate(myAnimation: _myAnimation), + children: _icons + .Select((IconData icon) => _buildItem(icon)).ToList() + ), + } + ) + ); + } + } +} \ No newline at end of file diff --git a/Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/FlowSample.cs.meta b/Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/FlowSample.cs.meta new file mode 100644 index 00000000..6c64caf0 --- /dev/null +++ b/Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/FlowSample.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e57994854884424f9a3dfc8b497ea914 +timeCreated: 1660548887 \ No newline at end of file diff --git a/com.unity.uiwidgets/Runtime/material/bottom_sheet.cs b/com.unity.uiwidgets/Runtime/material/bottom_sheet.cs index 9e2345f6..73a5de25 100644 --- a/com.unity.uiwidgets/Runtime/material/bottom_sheet.cs +++ b/com.unity.uiwidgets/Runtime/material/bottom_sheet.cs @@ -76,13 +76,11 @@ namespace Unity.UIWidgets.material { D.assert(builder != null); D.assert(debugCheckHasScaffold(context)); return Scaffold.of(context).showBottomSheet( - builder - //TODO: update showBottomSheet - // , - // backgroundColor: backgroundColor, - // elevation: elevation, - // shape: shape, - // clipBehavior: clipBehavior + builder, + backgroundColor: backgroundColor, + elevation: elevation, + shape: shape, + clipBehavior: clipBehavior ); } } @@ -111,6 +109,11 @@ namespace Unity.UIWidgets.material { this.elevation = elevation; this.onClosing = onClosing; this.builder = builder; + this.onDragStart = onDragStart; + this.onDragEnd = onDragEnd; + this.backgroundColor = backgroundColor; + this.shape = shape; + this.clipBehavior = clipBehavior; } public readonly AnimationController animationController; diff --git a/com.unity.uiwidgets/Runtime/rendering/flow.cs b/com.unity.uiwidgets/Runtime/rendering/flow.cs new file mode 100644 index 00000000..ccbe4767 --- /dev/null +++ b/com.unity.uiwidgets/Runtime/rendering/flow.cs @@ -0,0 +1,254 @@ +using System.Collections.Generic; +using Unity.UIWidgets.foundation; +using Unity.UIWidgets.ui; + +namespace Unity.UIWidgets.rendering { + public interface FlowPaintingContext { + Size size { get; } + + int childCount { get; } + + public Size getChildSize(int i); + + public void paintChild(int i, Matrix4 transform = null, float opacity = 1.0f); + } + + public abstract class FlowDelegate { + public FlowDelegate(Listenable repaint = null) { + _repaint = repaint; + } + + public readonly Listenable _repaint; + + public virtual Size getSize(BoxConstraints constraints) => constraints.biggest; + + public virtual BoxConstraints getConstraintsForChild(int i, BoxConstraints constraints) => constraints; + + public abstract void paintChildren(FlowPaintingContext context); + + public virtual bool shouldRelayout(FlowDelegate oldDelegate) => false; + + public abstract bool shouldRepaint(FlowDelegate oldDelegate); + + public override string ToString() => $"{this}, 'FlowDelegate'"; + } + + class FlowParentData : ContainerBoxParentData { + public Matrix4 _transform; + } + + class RenderFlow : RenderBoxContainerDefaultsMixinContainerRenderObjectMixinRenderBox + , FlowPaintingContext { + public RenderFlow( + List children = null, + FlowDelegate del = null) { + D.assert(del != null); + _delegate = del; + addAll(children); + } + + public override void setupParentData(RenderObject child) { + ParentData childParentData = child.parentData; + if (childParentData is FlowParentData flowParentData) { + flowParentData._transform = null; + } + else { + child.parentData = new FlowParentData(); + } + } + + public FlowDelegate del { + get { return _delegate; } + set { + D.assert(value != null); + if (_delegate == value) + return; + + FlowDelegate oldDelegate = _delegate; + _delegate = value; + + if (value.GetType() != oldDelegate.GetType() || value.shouldRelayout(oldDelegate)) + markNeedsLayout(); + else if (value.shouldRepaint(oldDelegate)) + markNeedsPaint(); + + if (attached) { + oldDelegate._repaint?.removeListener(markNeedsPaint); + value._repaint?.addListener(markNeedsPaint); + } + } + } + + public FlowDelegate _delegate; + + public override void attach(object owner) { + base.attach(owner); + _delegate._repaint?.addListener(markNeedsPaint); + } + + public override void detach() { + _delegate._repaint?.removeListener(markNeedsPaint); + base.detach(); + } + + Size _getSize(BoxConstraints constraints) { + D.assert(constraints.debugAssertIsValid()); + return constraints.constrain(_delegate.getSize(constraints)); + } + + public override bool isRepaintBoundary => true; + + + protected internal override float computeMinIntrinsicWidth(float height) { + float width = _getSize(BoxConstraints.tightForFinite(height: height)).width; + if (width.isFinite()) + return width; + return 0.0f; + } + + protected internal override float computeMaxIntrinsicWidth(float height) { + float width = _getSize(BoxConstraints.tightForFinite(height: height)).width; + if (width.isFinite()) + return width; + return 0.0f; + } + + protected internal override float computeMinIntrinsicHeight(float width) { + float height = _getSize(BoxConstraints.tightForFinite(width: width)).height; + if (height.isFinite()) + return height; + return 0.0f; + } + + protected internal override float computeMaxIntrinsicHeight(float width) { + float height = _getSize(BoxConstraints.tightForFinite(width: width)).height; + if (height.isFinite()) + return height; + return 0.0f; + } + + protected override void performLayout() { + BoxConstraints constraints = this.constraints; + size = _getSize(constraints); + int i = 0; + _randomAccessChildren.Clear(); + RenderBox child = firstChild; + while (child != null) { + _randomAccessChildren.Add(child); + BoxConstraints innerConstraints = _delegate.getConstraintsForChild(i, constraints); + child.layout(innerConstraints, parentUsesSize: true); + FlowParentData childParentData = child.parentData as FlowParentData; + childParentData.offset = Offset.zero; + child = childParentData.nextSibling; + i += 1; + } + } + + // Updated during layout. Only valid if layout is not dirty. + List _randomAccessChildren = new List(); + + // Updated during paint. + List _lastPaintOrder = new List(); + + // Only valid during paint. + PaintingContext _paintingContext; + Offset _paintingOffset; + + public Size getChildSize(int i) { + if (i < 0 || i >= _randomAccessChildren.Count) + return null; + return _randomAccessChildren[i].size; + } + + public void paintChild(int i, Matrix4 transform = null, float opacity = 1.0f) { + transform ??= Matrix4.identity(); + RenderBox child = _randomAccessChildren[i]; + FlowParentData childParentData = child.parentData as FlowParentData; + D.assert(() => { + if (childParentData._transform != null) { + throw new UIWidgetsError( + "Cannot call paintChild twice for the same child.\n" + + "The flow delegate of type ${_delegate.runtimeType} attempted to " + + "paint child $i multiple times, which is not permitted." + ); + } + + return true; + }); + _lastPaintOrder.Add(i); + childParentData._transform = transform; + + if (opacity == 0.0) + return; + + void painter(PaintingContext context, Offset offset) { + context.paintChild(child, offset); + } + + if (opacity == 1.0f) { + _paintingContext.pushTransform(needsCompositing, _paintingOffset, transform, painter); + } + else { + _paintingContext.pushOpacity(_paintingOffset, ui.Color.getAlphaFromOpacity(opacity), + (PaintingContext context, Offset offset) => { + context.pushTransform(needsCompositing, offset, transform, painter); + }); + } + } + + void _paintWithDelegate(PaintingContext context, Offset offset) { + _lastPaintOrder.Clear(); + _paintingContext = context; + _paintingOffset = offset; + foreach (RenderBox child in _randomAccessChildren) { + FlowParentData childParentData = child.parentData as FlowParentData; + childParentData._transform = null; + } + + try { + _delegate.paintChildren(this); + } + finally { + _paintingContext = null; + _paintingOffset = null; + } + } + + public override void paint(PaintingContext context, Offset offset) { + context.pushClipRect(needsCompositing, offset, Offset.zero & size, _paintWithDelegate); + } + + protected override bool hitTestChildren(BoxHitTestResult result, Offset position = null) { + List children = getChildrenAsList(); + for (int i = _lastPaintOrder.Count - 1; i >= 0; --i) { + int childIndex = _lastPaintOrder[i]; + if (childIndex >= children.Count) + continue; + RenderBox child = children[childIndex]; + FlowParentData childParentData = child.parentData as FlowParentData; + Matrix4 transform = childParentData._transform; + if (transform == null) + continue; + + bool absorbed = result.addWithPaintTransform( + transform: transform, + position: position, + hitTest: (BoxHitTestResult result, Offset position) => { + return child.hitTest(result, position: position); + } + ); + if (absorbed) + return true; + } + + return false; + } + + public override void applyPaintTransform(RenderObject child, Matrix4 transform) { + FlowParentData childParentData = child.parentData as FlowParentData; + if (childParentData._transform != null) + transform.multiply(childParentData._transform); + base.applyPaintTransform(child, transform); + } + } +} \ No newline at end of file diff --git a/com.unity.uiwidgets/Runtime/rendering/flow.cs.meta b/com.unity.uiwidgets/Runtime/rendering/flow.cs.meta new file mode 100644 index 00000000..689f2644 --- /dev/null +++ b/com.unity.uiwidgets/Runtime/rendering/flow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e9e4708564514c538ebfd4e3b226f66c +timeCreated: 1660546374 \ No newline at end of file diff --git a/com.unity.uiwidgets/Runtime/widgets/basic.cs b/com.unity.uiwidgets/Runtime/widgets/basic.cs index a6862f7a..7cd65419 100644 --- a/com.unity.uiwidgets/Runtime/widgets/basic.cs +++ b/com.unity.uiwidgets/Runtime/widgets/basic.cs @@ -2084,38 +2084,38 @@ namespace Unity.UIWidgets.widgets { throw new Exception("unknown axisDirection"); } } - - - /*public class Flow : MultiChildRenderObjectWidget { - + + public class Flow : MultiChildRenderObjectWidget { public Flow( Key key = null, FlowDelegate _delegate = null, List children = null - ) - : base(key: key, children: RepaintBoundary.wrapAll(children ?? new List())) { - D.assert(_delegate != null); - } + ) : base(key: key, children: children == null ? new List() : new List(RepaintBoundary.wrapAll(children))) + { + D.assert(_delegate != null); + this._delegate = _delegate; + } - public static Flow unwrapped( - Key key = null, - FlowDelegate _delegate = null, - List children = null - ) : base(key: key, children: children) { - D.assert(_delegate != null); - } + public Flow( + bool unwrapped, + Key key = null, + FlowDelegate _delegate = null, + List children = null + ) : base(key: key, children: children ?? new List()) + { + D.assert(_delegate != null); + this._delegate = _delegate; + } + + private readonly FlowDelegate _delegate; - /// The delegate that controls the transformation matrices of the children. - public readonly FlowDelegate _delegate; - - - public override RenderFlow createRenderObject(BuildContext context) => RenderFlow(_delegate: _delegate); + public override RenderObject createRenderObject(BuildContext context) => new RenderFlow(del: _delegate); - public override void updateRenderObject(BuildContext context, RenderFlow renderObject) { - renderObject._delegate = _delegate; - } - }*/ - + public override void updateRenderObject(BuildContext context, RenderObject renderObject) { + var renderFlow = renderObject as RenderFlow; + renderFlow.del = _delegate; + } + } public class RichText : MultiChildRenderObjectWidget { public RichText(