gewentao
6 年前
当前提交
0fe729c6
共有 51 个文件被更改,包括 4862 次插入 和 49 次删除
-
6.gitignore
-
5Assets/UIWidgets/Tests/Menu.cs
-
57Assets/UIWidgets/painting/basic_types.cs
-
118Assets/UIWidgets/rendering/object.mixin.gen.cs
-
17Assets/UIWidgets/rendering/object.mixin.njk
-
2Assets/UIWidgets/rendering/shifted_box.cs
-
514Assets/UIWidgets/rendering/sliver.cs
-
736Assets/UIWidgets/rendering/viewpoint.cs
-
61Assets/UIWidgets/rendering/viewport_offset.cs
-
35Assets/UIWidgets/ui/geometry.cs
-
10Assets/UIWidgets/ui/painting/canvas.cs
-
15Assets/UIWidgets/ui/painting/canvas_impl.cs
-
8Assets/UIWidgets/ui/painting/draw_cmd.cs
-
8Assets/UIWidgets/ui/painting/picture.cs
-
401Assets/UIWidgets/ui/text.cs
-
70Assets/UIWidgets/Resources/UIWidgets_Text.shader
-
3Assets/UIWidgets/Resources/UIWidgets_Text.shader.meta
-
152Assets/UIWidgets/Tests/Paragraph.cs
-
3Assets/UIWidgets/Tests/Paragraph.cs.meta
-
3Assets/UIWidgets/math.meta
-
288Assets/UIWidgets/painting/text_painter.cs
-
3Assets/UIWidgets/painting/text_painter.cs.meta
-
246Assets/UIWidgets/painting/text_span.cs
-
3Assets/UIWidgets/painting/text_span.cs.meta
-
86Assets/UIWidgets/painting/text_style.cs
-
3Assets/UIWidgets/painting/text_style.cs.meta
-
235Assets/UIWidgets/rendering/paragraph.cs
-
3Assets/UIWidgets/rendering/paragraph.cs.meta
-
170Assets/UIWidgets/rendering/sliver_fixed_extent_list.cs
-
3Assets/UIWidgets/rendering/sliver_fixed_extent_list.cs.meta
-
194Assets/UIWidgets/rendering/sliver_list.cs
-
3Assets/UIWidgets/rendering/sliver_list.cs.meta
-
299Assets/UIWidgets/rendering/sliver_multi_box_adaptor.cs
-
3Assets/UIWidgets/rendering/sliver_multi_box_adaptor.cs.meta
-
232Assets/UIWidgets/rendering/sliver_padding.cs
-
3Assets/UIWidgets/rendering/sliver_padding.cs.meta
-
3Assets/UIWidgets/ui/txt.meta
-
2Assets/UIWidgets/gestures/arena.cs
-
14Assets/UIWidgets/math/math.cs
-
3Assets/UIWidgets/math/math.cs.meta
-
3Assets/UIWidgets/ui/txt/linebreaker.cs.meta
-
3Assets/UIWidgets/ui/txt/paragraph.cs.meta
-
3Assets/UIWidgets/ui/txt/paragraph_builder.cs.meta
-
161Assets/UIWidgets/ui/txt/styled_runs.cs
-
3Assets/UIWidgets/ui/txt/styled_runs.cs.meta
-
152Assets/UIWidgets/ui/txt/linebreaker.cs
-
490Assets/UIWidgets/ui/txt/paragraph.cs
-
76Assets/UIWidgets/ui/txt/paragraph_builder.cs
|
|||
namespace UIWidgets.rendering { |
|||
using System; |
|||
using UIWidgets.painting; |
|||
using UIWidgets.ui; |
|||
using UnityEngine; |
|||
using Rect = UIWidgets.ui.Rect; |
|||
|
|||
namespace UIWidgets.rendering { |
|||
public enum GrowthDirection { |
|||
forward, |
|||
reverse, |
|||
} |
|||
|
|||
public static class GrowthDirectionUtils { |
|||
public static AxisDirection applyGrowthDirectionToAxisDirection( |
|||
AxisDirection axisDirection, GrowthDirection growthDirection) { |
|||
switch (growthDirection) { |
|||
case GrowthDirection.forward: |
|||
return axisDirection; |
|||
case GrowthDirection.reverse: |
|||
return AxisUtils.flipAxisDirection(axisDirection); |
|||
} |
|||
|
|||
throw new Exception("unknown growthDirection"); |
|||
} |
|||
|
|||
public static ScrollDirection applyGrowthDirectionToScrollDirection( |
|||
ScrollDirection scrollDirection, GrowthDirection growthDirection) { |
|||
switch (growthDirection) { |
|||
case GrowthDirection.forward: |
|||
return scrollDirection; |
|||
case GrowthDirection.reverse: |
|||
return ScrollDirectionUtils.flipScrollDirection(scrollDirection); |
|||
} |
|||
public class SliverPhysicalParentData : ContainerParentDataMixinParentData<RenderSliver> { |
|||
throw new Exception("unknown growthDirection"); |
|||
} |
|||
public class SliverPhysicalContainerParentData : SliverPhysicalParentData { |
|||
public class SliverConstraints : Constraints, IEquatable<SliverConstraints> { |
|||
public SliverConstraints( |
|||
AxisDirection axisDirection, |
|||
GrowthDirection growthDirection, |
|||
ScrollDirection userScrollDirection, |
|||
double scrollOffset, |
|||
double overlap, |
|||
double remainingPaintExtent, |
|||
double crossAxisExtent, |
|||
AxisDirection crossAxisDirection, |
|||
double viewportMainAxisExtent, |
|||
double remainingCacheExtent, |
|||
double cacheOrigin |
|||
) { |
|||
this.axisDirection = axisDirection; |
|||
this.growthDirection = growthDirection; |
|||
this.userScrollDirection = userScrollDirection; |
|||
this.scrollOffset = scrollOffset; |
|||
this.overlap = overlap; |
|||
this.remainingPaintExtent = remainingPaintExtent; |
|||
this.crossAxisExtent = crossAxisExtent; |
|||
this.crossAxisDirection = crossAxisDirection; |
|||
this.viewportMainAxisExtent = viewportMainAxisExtent; |
|||
this.remainingCacheExtent = remainingCacheExtent; |
|||
this.cacheOrigin = cacheOrigin; |
|||
} |
|||
|
|||
public SliverConstraints copyWith( |
|||
AxisDirection? axisDirection = null, |
|||
GrowthDirection? growthDirection = null, |
|||
ScrollDirection? userScrollDirection = null, |
|||
double? scrollOffset = null, |
|||
double? overlap = null, |
|||
double? remainingPaintExtent = null, |
|||
double? crossAxisExtent = null, |
|||
AxisDirection? crossAxisDirection = null, |
|||
double? viewportMainAxisExtent = null, |
|||
double? remainingCacheExtent = null, |
|||
double? cacheOrigin = null |
|||
) { |
|||
return new SliverConstraints( |
|||
axisDirection: axisDirection ?? this.axisDirection, |
|||
growthDirection: growthDirection ?? this.growthDirection, |
|||
userScrollDirection: userScrollDirection ?? this.userScrollDirection, |
|||
scrollOffset: scrollOffset ?? this.scrollOffset, |
|||
overlap: overlap ?? this.overlap, |
|||
remainingPaintExtent: remainingPaintExtent ?? this.remainingPaintExtent, |
|||
crossAxisExtent: crossAxisExtent ?? this.crossAxisExtent, |
|||
crossAxisDirection: crossAxisDirection ?? this.crossAxisDirection, |
|||
viewportMainAxisExtent: viewportMainAxisExtent ?? this.viewportMainAxisExtent, |
|||
remainingCacheExtent: remainingCacheExtent ?? this.remainingCacheExtent, |
|||
cacheOrigin: cacheOrigin ?? this.cacheOrigin |
|||
); |
|||
} |
|||
|
|||
public readonly AxisDirection axisDirection; |
|||
|
|||
public readonly GrowthDirection growthDirection; |
|||
|
|||
public readonly ScrollDirection userScrollDirection; |
|||
|
|||
public readonly double scrollOffset; |
|||
|
|||
public readonly double overlap; |
|||
|
|||
public readonly double remainingPaintExtent; |
|||
|
|||
public readonly double crossAxisExtent; |
|||
|
|||
public readonly AxisDirection crossAxisDirection; |
|||
|
|||
public readonly double viewportMainAxisExtent; |
|||
|
|||
public readonly double cacheOrigin; |
|||
|
|||
public readonly double remainingCacheExtent; |
|||
|
|||
public Axis axis { |
|||
get { return AxisUtils.axisDirectionToAxis(this.axisDirection); } |
|||
} |
|||
|
|||
public GrowthDirection normalizedGrowthDirection { |
|||
get { |
|||
switch (this.axisDirection) { |
|||
case AxisDirection.down: |
|||
case AxisDirection.right: |
|||
return this.growthDirection; |
|||
case AxisDirection.up: |
|||
case AxisDirection.left: |
|||
switch (this.growthDirection) { |
|||
case GrowthDirection.forward: |
|||
return GrowthDirection.reverse; |
|||
case GrowthDirection.reverse: |
|||
return GrowthDirection.forward; |
|||
} |
|||
|
|||
throw new Exception("unknown growthDirection"); |
|||
} |
|||
|
|||
throw new Exception("unknown axisDirection"); |
|||
} |
|||
} |
|||
|
|||
public override bool isTight { |
|||
get { return false; } |
|||
} |
|||
|
|||
public override bool isNormalized { |
|||
get { |
|||
return this.scrollOffset >= 0.0 |
|||
&& this.crossAxisExtent >= 0.0 |
|||
&& AxisUtils.axisDirectionToAxis(this.axisDirection) != |
|||
AxisUtils.axisDirectionToAxis(this.crossAxisDirection) |
|||
&& this.viewportMainAxisExtent >= 0.0 |
|||
&& this.remainingPaintExtent >= 0.0; |
|||
} |
|||
} |
|||
|
|||
public BoxConstraints asBoxConstraints( |
|||
double minExtent = 0.0, |
|||
double maxExtent = double.PositiveInfinity, |
|||
double? crossAxisExtent = null |
|||
) { |
|||
crossAxisExtent = crossAxisExtent ?? this.crossAxisExtent; |
|||
switch (this.axis) { |
|||
case Axis.horizontal: |
|||
return new BoxConstraints( |
|||
minHeight: crossAxisExtent.Value, |
|||
maxHeight: crossAxisExtent.Value, |
|||
minWidth: minExtent, |
|||
maxWidth: maxExtent |
|||
); |
|||
case Axis.vertical: |
|||
return new BoxConstraints( |
|||
minWidth: crossAxisExtent.Value, |
|||
maxWidth: crossAxisExtent.Value, |
|||
minHeight: minExtent, |
|||
maxHeight: maxExtent |
|||
); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
public bool Equals(SliverConstraints other) { |
|||
if (object.ReferenceEquals(null, other)) return false; |
|||
if (object.ReferenceEquals(this, other)) return true; |
|||
return this.axisDirection == other.axisDirection |
|||
&& this.growthDirection == other.growthDirection |
|||
&& this.userScrollDirection == other.userScrollDirection |
|||
&& this.scrollOffset.Equals(other.scrollOffset) |
|||
&& this.overlap.Equals(other.overlap) |
|||
&& this.remainingPaintExtent.Equals(other.remainingPaintExtent) |
|||
&& this.crossAxisExtent.Equals(other.crossAxisExtent) |
|||
&& this.crossAxisDirection == other.crossAxisDirection |
|||
&& this.viewportMainAxisExtent.Equals(other.viewportMainAxisExtent) |
|||
&& this.cacheOrigin.Equals(other.cacheOrigin) |
|||
&& this.remainingCacheExtent.Equals(other.remainingCacheExtent); |
|||
} |
|||
|
|||
public override bool Equals(object obj) { |
|||
if (object.ReferenceEquals(null, obj)) return false; |
|||
if (object.ReferenceEquals(this, obj)) return true; |
|||
if (obj.GetType() != this.GetType()) return false; |
|||
return this.Equals((SliverConstraints) obj); |
|||
} |
|||
|
|||
public override int GetHashCode() { |
|||
unchecked { |
|||
var hashCode = (int) this.axisDirection; |
|||
hashCode = (hashCode * 397) ^ (int) this.growthDirection; |
|||
hashCode = (hashCode * 397) ^ (int) this.userScrollDirection; |
|||
hashCode = (hashCode * 397) ^ this.scrollOffset.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ this.overlap.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ this.remainingPaintExtent.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ this.crossAxisExtent.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ (int) this.crossAxisDirection; |
|||
hashCode = (hashCode * 397) ^ this.viewportMainAxisExtent.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ this.cacheOrigin.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ this.remainingCacheExtent.GetHashCode(); |
|||
return hashCode; |
|||
} |
|||
} |
|||
|
|||
public static bool operator ==(SliverConstraints left, SliverConstraints right) { |
|||
return Equals(left, right); |
|||
} |
|||
|
|||
public static bool operator !=(SliverConstraints left, SliverConstraints right) { |
|||
return !Equals(left, right); |
|||
} |
|||
|
|||
|
|||
public class SliverGeometry { |
|||
public SliverGeometry( |
|||
double scrollExtent = 0.0, |
|||
double paintExtent = 0.0, |
|||
double paintOrigin = 0.0, |
|||
double? layoutExtent = null, |
|||
double maxPaintExtent = 0.0, |
|||
double maxScrollObstructionExtent = 0.0, |
|||
double? hitTestExtent = null, |
|||
bool? visible = null, |
|||
bool hasVisualOverflow = false, |
|||
double scrollOffsetCorrection = 0.0, |
|||
double? cacheExtent = null |
|||
) { |
|||
this.scrollExtent = scrollExtent; |
|||
this.paintExtent = paintExtent; |
|||
this.paintOrigin = paintOrigin; |
|||
this.layoutExtent = layoutExtent ?? paintExtent; |
|||
this.maxPaintExtent = maxPaintExtent; |
|||
this.maxScrollObstructionExtent = maxScrollObstructionExtent; |
|||
this.hitTestExtent = hitTestExtent ?? paintExtent; |
|||
this.visible = visible ?? paintExtent > 0.0; |
|||
this.hasVisualOverflow = hasVisualOverflow; |
|||
this.scrollOffsetCorrection = scrollOffsetCorrection; |
|||
this.cacheExtent = cacheExtent ?? layoutExtent ?? paintExtent; |
|||
} |
|||
|
|||
public static readonly SliverGeometry zero = new SliverGeometry(); |
|||
|
|||
public readonly double scrollExtent; |
|||
public readonly double paintOrigin; |
|||
public readonly double paintExtent; |
|||
public readonly double layoutExtent; |
|||
public readonly double maxPaintExtent; |
|||
public readonly double maxScrollObstructionExtent; |
|||
public readonly double hitTestExtent; |
|||
public readonly bool visible; |
|||
public readonly bool hasVisualOverflow; |
|||
public readonly double scrollOffsetCorrection; |
|||
public readonly double cacheExtent; |
|||
} |
|||
|
|||
public class SliverPhysicalParentData : ParentData { |
|||
public Offset paintOffset = Offset.zero; |
|||
|
|||
public void applyPaintTransform(ref Matrix4x4 transform) { |
|||
transform = Matrix4x4.Translate(this.paintOffset.toVector()) * transform; |
|||
} |
|||
} |
|||
|
|||
public class SliverPhysicalContainerParentData : ContainerParentDataMixinSliverPhysicalParentData<RenderSliver> { |
|||
} |
|||
|
|||
public class SliverLogicalParentData : ParentData { |
|||
public double layoutOffset = 0.0; |
|||
} |
|||
|
|||
public class SliverLogicalContainerParentData : ContainerParentDataMixinSliverLogicalParentData<RenderSliver> { |
|||
} |
|||
|
|||
|
|||
public new SliverConstraints constraints { |
|||
get { return (SliverConstraints) base.constraints; } |
|||
} |
|||
|
|||
public SliverGeometry geometry { |
|||
get { return this._geometry; } |
|||
set { this._geometry = value; } |
|||
} |
|||
|
|||
public SliverGeometry _geometry; |
|||
|
|||
public override Rect paintBounds { |
|||
get { |
|||
switch (this.constraints.axis) { |
|||
case Axis.horizontal: |
|||
return Rect.fromLTWH( |
|||
0.0, 0.0, |
|||
this.geometry.paintExtent, |
|||
this.constraints.crossAxisExtent |
|||
); |
|||
case Axis.vertical: |
|||
return Rect.fromLTWH( |
|||
0.0, 0.0, |
|||
this.constraints.crossAxisExtent, |
|||
this.geometry.paintExtent |
|||
); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public override void performResize() { |
|||
} |
|||
|
|||
public double centerOffsetAdjustment { |
|||
get { return 0.0; } |
|||
} |
|||
|
|||
public double calculatePaintOffset(SliverConstraints constraints, double from, double to) { |
|||
double a = constraints.scrollOffset; |
|||
double b = constraints.scrollOffset + constraints.remainingPaintExtent; |
|||
return (to.clamp(a, b) - from.clamp(a, b)).clamp(0.0, constraints.remainingPaintExtent); |
|||
} |
|||
|
|||
public double calculateCacheOffset(SliverConstraints constraints, double from, double to) { |
|||
double a = constraints.scrollOffset + constraints.cacheOrigin; |
|||
double b = constraints.scrollOffset + constraints.remainingCacheExtent; |
|||
return (to.clamp(a, b) - from.clamp(a, b)).clamp(0.0, constraints.remainingCacheExtent); |
|||
} |
|||
|
|||
public virtual double childMainAxisPosition(RenderObject child) { |
|||
return 0.0; |
|||
} |
|||
|
|||
public virtual double childCrossAxisPosition(RenderObject child) { |
|||
return 0.0; |
|||
} |
|||
|
|||
public virtual double childScrollOffset(RenderObject child) { |
|||
return 0.0; |
|||
} |
|||
|
|||
public override void applyPaintTransform(RenderObject child, ref Matrix4x4 transform) { |
|||
} |
|||
|
|||
public Size getAbsoluteSizeRelativeToOrigin() { |
|||
switch (GrowthDirectionUtils.applyGrowthDirectionToAxisDirection( |
|||
this.constraints.axisDirection, this.constraints.growthDirection)) { |
|||
case AxisDirection.up: |
|||
return new Size(this.constraints.crossAxisExtent, -this.geometry.paintExtent); |
|||
case AxisDirection.right: |
|||
return new Size(this.geometry.paintExtent, this.constraints.crossAxisExtent); |
|||
case AxisDirection.down: |
|||
return new Size(this.constraints.crossAxisExtent, this.geometry.paintExtent); |
|||
case AxisDirection.left: |
|||
return new Size(-this.geometry.paintExtent, this.constraints.crossAxisExtent); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public static class RenderSliverHelpers { |
|||
public static bool _getRightWayUp(SliverConstraints constraints) { |
|||
bool rightWayUp = true; |
|||
switch (constraints.axisDirection) { |
|||
case AxisDirection.up: |
|||
case AxisDirection.left: |
|||
rightWayUp = false; |
|||
break; |
|||
case AxisDirection.down: |
|||
case AxisDirection.right: |
|||
rightWayUp = true; |
|||
break; |
|||
} |
|||
|
|||
switch (constraints.growthDirection) { |
|||
case GrowthDirection.forward: |
|||
break; |
|||
case GrowthDirection.reverse: |
|||
rightWayUp = !rightWayUp; |
|||
break; |
|||
} |
|||
|
|||
return rightWayUp; |
|||
} |
|||
|
|||
public static void applyPaintTransformForBoxChild(this RenderSliver it, RenderBox child, |
|||
ref Matrix4x4 transform) { |
|||
bool rightWayUp = RenderSliverHelpers._getRightWayUp(it.constraints); |
|||
double delta = it.childMainAxisPosition(child); |
|||
double crossAxisDelta = it.childCrossAxisPosition(child); |
|||
switch (it.constraints.axis) { |
|||
case Axis.horizontal: |
|||
if (!rightWayUp) { |
|||
delta = it.geometry.paintExtent - child.size.width - delta; |
|||
} |
|||
|
|||
transform = Matrix4x4.Translate(new Vector2((float) delta, (float) crossAxisDelta)) * transform; |
|||
break; |
|||
case Axis.vertical: |
|||
if (!rightWayUp) { |
|||
delta = it.geometry.paintExtent - child.size.height - delta; |
|||
} |
|||
|
|||
transform = Matrix4x4.Translate(new Vector2((float) crossAxisDelta, (float) delta)) * transform; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public abstract class RenderSliverSingleBoxAdapter : RenderObjectWithChildMixinRenderSliver<RenderBox> { |
|||
public RenderSliverSingleBoxAdapter( |
|||
RenderBox child = null |
|||
) { |
|||
this.child = child; |
|||
} |
|||
|
|||
public override void setupParentData(RenderObject child) { |
|||
if (!(child.parentData is SliverPhysicalParentData)) { |
|||
child.parentData = new SliverPhysicalParentData(); |
|||
} |
|||
} |
|||
|
|||
public void setChildParentData(RenderObject child, SliverConstraints constraints, SliverGeometry geometry) { |
|||
var childParentData = (SliverPhysicalParentData) child.parentData; |
|||
switch (GrowthDirectionUtils.applyGrowthDirectionToAxisDirection(constraints.axisDirection, |
|||
constraints.growthDirection)) { |
|||
case AxisDirection.up: |
|||
childParentData.paintOffset = new Offset(0.0, |
|||
-(geometry.scrollExtent - (geometry.paintExtent + constraints.scrollOffset))); |
|||
break; |
|||
case AxisDirection.right: |
|||
childParentData.paintOffset = new Offset(-constraints.scrollOffset, 0.0); |
|||
break; |
|||
case AxisDirection.down: |
|||
childParentData.paintOffset = new Offset(0.0, -constraints.scrollOffset); |
|||
break; |
|||
case AxisDirection.left: |
|||
childParentData.paintOffset = |
|||
new Offset(-(geometry.scrollExtent - (geometry.paintExtent + constraints.scrollOffset)), 0.0); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
public override double childMainAxisPosition(RenderObject child) { |
|||
return -this.constraints.scrollOffset; |
|||
} |
|||
|
|||
public override void applyPaintTransform(RenderObject child, ref Matrix4x4 transform) { |
|||
var childParentData = (SliverPhysicalParentData) child.parentData; |
|||
childParentData.applyPaintTransform(ref transform); |
|||
} |
|||
|
|||
public override void paint(PaintingContext context, Offset offset) { |
|||
if (this.child != null && this.geometry.visible) { |
|||
var childParentData = (SliverPhysicalParentData) this.child.parentData; |
|||
context.paintChild(this.child, offset + childParentData.paintOffset); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public class RenderSliverToBoxAdapter : RenderSliverSingleBoxAdapter { |
|||
public RenderSliverToBoxAdapter( |
|||
RenderBox child = null |
|||
) : base(child) { |
|||
} |
|||
|
|||
public override void performLayout() { |
|||
if (this.child == null) { |
|||
this.geometry = SliverGeometry.zero; |
|||
return; |
|||
} |
|||
|
|||
this.child.layout(this.constraints.asBoxConstraints(), parentUsesSize: true); |
|||
|
|||
double childExtent = 0.0; |
|||
switch (this.constraints.axis) { |
|||
case Axis.horizontal: |
|||
childExtent = this.child.size.width; |
|||
break; |
|||
case Axis.vertical: |
|||
childExtent = this.child.size.height; |
|||
break; |
|||
} |
|||
|
|||
double paintedChildSize = this.calculatePaintOffset(this.constraints, from: 0.0, to: childExtent); |
|||
double cacheExtent = this.calculateCacheOffset(this.constraints, from: 0.0, to: childExtent); |
|||
|
|||
this.geometry = new SliverGeometry( |
|||
scrollExtent: childExtent, |
|||
paintExtent: paintedChildSize, |
|||
cacheExtent: cacheExtent, |
|||
maxPaintExtent: childExtent, |
|||
hitTestExtent: paintedChildSize, |
|||
hasVisualOverflow: childExtent > this.constraints.remainingPaintExtent |
|||
|| this.constraints.scrollOffset > 0.0 |
|||
); |
|||
|
|||
this.setChildParentData(this.child, this.constraints, this.geometry); |
|||
} |
|||
} |
|||
} |
|
|||
namespace UIWidgets.ui { |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using JetBrains.Annotations; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgets.ui { |
|||
|
|||
public enum FontStyle { |
|||
/// Use the upright glyphs
|
|||
normal, |
|||
|
|||
/// Use glyphs designed for slanting
|
|||
italic, |
|||
} |
|||
|
|||
|
|||
public enum TextAlign { |
|||
/// Align the text on the left edge of the container.
|
|||
left, |
|||
|
|||
/// Align the text on the right edge of the container.
|
|||
right, |
|||
|
|||
/// Align the text in the center of the container.
|
|||
center, |
|||
|
|||
/// Stretch lines of text that end with a soft line break to fill the width of
|
|||
/// the container.
|
|||
///
|
|||
/// Lines that end with hard line breaks are aligned towards the [start] edge.
|
|||
justify, |
|||
} |
|||
|
|||
public class ParagraphConstraints: IEquatable<ParagraphConstraints> |
|||
{ |
|||
public readonly double width; |
|||
|
|||
public ParagraphConstraints(double width) |
|||
{ |
|||
this.width = width; |
|||
} |
|||
|
|||
public bool Equals(ParagraphConstraints other) |
|||
{ |
|||
if (ReferenceEquals(null, other)) return false; |
|||
if (ReferenceEquals(this, other)) return true; |
|||
return width.Equals(other.width); |
|||
} |
|||
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (ReferenceEquals(null, obj)) return false; |
|||
if (ReferenceEquals(this, obj)) return true; |
|||
if (obj.GetType() != this.GetType()) return false; |
|||
return Equals((ParagraphConstraints) obj); |
|||
} |
|||
|
|||
public override int GetHashCode() |
|||
{ |
|||
return width.GetHashCode(); |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return string.Format("Width: {0}", width); |
|||
} |
|||
} |
|||
|
|||
public class TextStyle:IEquatable<TextStyle> |
|||
{ |
|||
public static readonly string defaultFontFamily = "Helvetica"; |
|||
public static readonly double defaultFontSize = 14.0; |
|||
public static readonly FontWeight defaultFontWeight = FontWeight.w400; |
|||
public static readonly FontStyle defaultFontStyle = FontStyle.normal; |
|||
public static readonly Color defaultColor = Color.fromARGB(255, 0, 0, 0); |
|||
public Color color; |
|||
public double? fontSize; |
|||
public FontWeight? fontWeight; |
|||
public FontStyle? fontStyle; |
|||
public double? letterSpacing; |
|||
public double? wordSpacing; |
|||
public TextBaseline? textBaseline; |
|||
public double? height; |
|||
public TextDecoration decoration; |
|||
public string fontFamily; |
|||
|
|||
public FontStyle safeFontStyle |
|||
{ |
|||
get { return fontStyle ?? defaultFontStyle; } |
|||
} |
|||
|
|||
public string safeFontFamily |
|||
{ |
|||
get { return fontFamily ?? defaultFontFamily; } |
|||
} |
|||
|
|||
public double safeFontSize |
|||
{ |
|||
get { return fontSize ?? defaultFontSize; } |
|||
} |
|||
|
|||
public FontWeight safeFontWeight |
|||
{ |
|||
get { return fontWeight ?? defaultFontWeight; } |
|||
} |
|||
|
|||
public UnityEngine.Color UnityColor |
|||
{ |
|||
get { return (color ?? defaultColor).toColor(); } |
|||
} |
|||
|
|||
public UnityEngine.FontStyle UnityFontStyle |
|||
{ |
|||
get |
|||
{ |
|||
if (safeFontStyle == FontStyle.italic) |
|||
{ |
|||
if (safeFontWeight == FontWeight.w700) |
|||
{ |
|||
return UnityEngine.FontStyle.BoldAndItalic; |
|||
} |
|||
else |
|||
{ |
|||
return UnityEngine.FontStyle.Italic; |
|||
} |
|||
} else if (safeFontWeight == FontWeight.w700) |
|||
{ |
|||
return UnityEngine.FontStyle.Bold; |
|||
} |
|||
|
|||
return UnityEngine.FontStyle.Normal; |
|||
} |
|||
} |
|||
|
|||
public int UnityFontSize |
|||
{ |
|||
get { return (int) safeFontSize; } |
|||
} |
|||
|
|||
public TextStyle merge(TextStyle style) |
|||
{ |
|||
var ret = new TextStyle(); |
|||
ret.color = style.color??color; |
|||
ret.fontSize = style.fontSize??fontSize; |
|||
ret.fontWeight = style.fontWeight??fontWeight; |
|||
ret.fontStyle = style.fontStyle??fontStyle; |
|||
ret.letterSpacing = style.letterSpacing??letterSpacing; |
|||
ret.textBaseline = style.textBaseline??textBaseline; |
|||
ret.height = style.height??height; |
|||
ret.decoration = style.decoration??decoration; |
|||
ret.fontFamily = style.fontFamily??fontFamily; |
|||
return ret; |
|||
} |
|||
|
|||
public bool Equals(TextStyle other) |
|||
{ |
|||
if (ReferenceEquals(null, other)) return false; |
|||
if (ReferenceEquals(this, other)) return true; |
|||
return color == other.color && fontSize == other.fontSize && fontWeight == other.fontWeight && |
|||
fontStyle == other.fontStyle && letterSpacing == other.letterSpacing && |
|||
wordSpacing == other.wordSpacing && textBaseline == other.textBaseline && |
|||
height == other.height && decoration == other.decoration && fontFamily == other.fontFamily; |
|||
} |
|||
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (ReferenceEquals(null, obj)) return false; |
|||
if (ReferenceEquals(this, obj)) return true; |
|||
if (obj.GetType() != this.GetType()) return false; |
|||
return Equals((TextStyle) obj); |
|||
} |
|||
|
|||
public override int GetHashCode() |
|||
{ |
|||
unchecked |
|||
{ |
|||
var hashCode = (color != null ? color.GetHashCode() : 0); |
|||
hashCode = (hashCode * 397) ^ fontSize.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ fontWeight.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ fontStyle.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ letterSpacing.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ wordSpacing.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ textBaseline.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ height.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ (decoration != null ? decoration.GetHashCode() : 0); |
|||
hashCode = (hashCode * 397) ^ (fontFamily != null ? fontFamily.GetHashCode() : 0); |
|||
return hashCode; |
|||
} |
|||
} |
|||
|
|||
public static bool operator ==(TextStyle left, TextStyle right) |
|||
{ |
|||
return Equals(left, right); |
|||
} |
|||
|
|||
public static bool operator !=(TextStyle left, TextStyle right) |
|||
{ |
|||
return !Equals(left, right); |
|||
} |
|||
|
|||
public TextStyle(Color color = null, double? fontSize = default(double?), FontWeight? fontWeight = default(FontWeight?), FontStyle? fontStyle = default(FontStyle?), double? letterSpacing = default(double?), double? wordSpacing = default(double?), TextBaseline? textBaseline = default(TextBaseline?), double? height = default(double?), TextDecoration decoration = null, string fontFamily = null) |
|||
{ |
|||
this.color = color; |
|||
this.fontSize = fontSize; |
|||
this.fontWeight = fontWeight; |
|||
this.fontStyle = fontStyle; |
|||
this.letterSpacing = letterSpacing; |
|||
this.wordSpacing = wordSpacing; |
|||
this.textBaseline = textBaseline; |
|||
this.height = height; |
|||
this.decoration = decoration; |
|||
this.fontFamily = fontFamily; |
|||
} |
|||
} |
|||
|
|||
public class ParagraphStyle: IEquatable<ParagraphStyle> |
|||
{ |
|||
|
|||
public ParagraphStyle(TextAlign? textAlign = null, |
|||
TextDirection? textDirection = null, |
|||
FontWeight? fontWeight = null, |
|||
FontStyle? fontStyle = null, |
|||
int? maxLines = null, |
|||
double? fontSize = null, |
|||
string fontFamily = null, |
|||
double? lineHeight = null, // todo
|
|||
string ellipsis = null) |
|||
{ |
|||
this.textAlign = textAlign; |
|||
this.textDirection = textDirection; |
|||
this.fontWeight = fontWeight; |
|||
this.fontStyle = fontStyle; |
|||
this.maxLines = maxLines; |
|||
this.fontSize = fontSize; |
|||
this.fontFamily = fontFamily; |
|||
this.lineHeight = lineHeight; |
|||
this.ellipsis = ellipsis; |
|||
} |
|||
|
|||
public bool Equals(ParagraphStyle other) |
|||
{ |
|||
if (ReferenceEquals(null, other)) return false; |
|||
if (ReferenceEquals(this, other)) return true; |
|||
return textAlign == other.textAlign && textDirection == other.textDirection && fontWeight == other.fontWeight && fontStyle == other.fontStyle && maxLines == other.maxLines && fontSize.Equals(other.fontSize) && string.Equals(fontFamily, other.fontFamily) && lineHeight.Equals(other.lineHeight) && string.Equals(ellipsis, other.ellipsis); |
|||
} |
|||
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (ReferenceEquals(null, obj)) return false; |
|||
if (ReferenceEquals(this, obj)) return true; |
|||
if (obj.GetType() != this.GetType()) return false; |
|||
return Equals((ParagraphStyle) obj); |
|||
} |
|||
|
|||
public static bool operator ==(ParagraphStyle a, ParagraphStyle b) { |
|||
return Equals(a, b); |
|||
} |
|||
|
|||
public static bool operator !=(ParagraphStyle a, ParagraphStyle b) { |
|||
return !(a == b); |
|||
} |
|||
|
|||
public override int GetHashCode() |
|||
{ |
|||
unchecked |
|||
{ |
|||
var hashCode = textAlign.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ textDirection.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ fontWeight.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ fontStyle.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ maxLines.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ fontSize.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ (fontFamily != null ? fontFamily.GetHashCode() : 0); |
|||
hashCode = (hashCode * 397) ^ lineHeight.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ (ellipsis != null ? ellipsis.GetHashCode() : 0); |
|||
return hashCode; |
|||
} |
|||
} |
|||
|
|||
public TextStyle getTextStyle() |
|||
{ |
|||
return new TextStyle( |
|||
fontWeight: fontWeight, |
|||
fontStyle: fontStyle, |
|||
fontFamily: fontFamily, |
|||
fontSize: fontSize, |
|||
height: lineHeight |
|||
); |
|||
} |
|||
|
|||
public readonly TextAlign? textAlign; |
|||
public readonly TextDirection? textDirection; |
|||
public readonly FontWeight? fontWeight; |
|||
public readonly FontStyle? fontStyle; |
|||
public readonly int? maxLines; |
|||
public readonly double? fontSize; |
|||
public readonly string fontFamily; |
|||
public readonly double? lineHeight; |
|||
public readonly string ellipsis; |
|||
} |
|||
|
|||
public class TextDecoration: IEquatable<TextDecoration> |
|||
{ |
|||
public bool Equals(TextDecoration other) |
|||
{ |
|||
if (ReferenceEquals(null, other)) return false; |
|||
if (ReferenceEquals(this, other)) return true; |
|||
return mask == other.mask; |
|||
} |
|||
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (ReferenceEquals(null, obj)) return false; |
|||
if (ReferenceEquals(this, obj)) return true; |
|||
if (obj.GetType() != this.GetType()) return false; |
|||
return Equals((TextDecoration) obj); |
|||
} |
|||
|
|||
public override int GetHashCode() |
|||
{ |
|||
return mask; |
|||
} |
|||
|
|||
|
|||
public static bool operator ==(TextDecoration a, TextDecoration b) { |
|||
return Equals(a, b); |
|||
} |
|||
|
|||
public static bool operator !=(TextDecoration a, TextDecoration b) { |
|||
return !(a == b); |
|||
} |
|||
|
|||
public static readonly TextDecoration none = new TextDecoration(0); |
|||
|
|||
public static readonly TextDecoration underline = new TextDecoration(1); |
|||
|
|||
public static readonly TextDecoration overline = new TextDecoration(2); |
|||
|
|||
public static readonly TextDecoration lineThrough = new TextDecoration(4); |
|||
|
|||
public readonly int mask; |
|||
|
|||
public TextDecoration(int mask) |
|||
{ |
|||
this.mask = mask; |
|||
} |
|||
|
|||
bool contains(TextDecoration other) { |
|||
return (mask | other.mask) == mask; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public enum TextAffinity { |
|||
upstream, |
|||
downstream, |
|||
} |
|||
|
|||
public enum FontWeight |
|||
{ |
|||
w400, // normal
|
|||
w700, // bold
|
|||
} |
|||
|
|||
public class TextPosition |
|||
{ |
|||
public readonly int offset; |
|||
public readonly TextAffinity affinity; |
|||
|
|||
public TextPosition(int offset, TextAffinity affinity = TextAffinity.downstream) |
|||
{ |
|||
this.offset = offset; |
|||
this.affinity = affinity; |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return string.Format("Offset: {0}, Affinity: {1}", offset, affinity); |
|||
} |
|||
|
|||
protected bool Equals(TextPosition other) |
|||
{ |
|||
return offset == other.offset && affinity == other.affinity; |
|||
} |
|||
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (ReferenceEquals(null, obj)) return false; |
|||
if (ReferenceEquals(this, obj)) return true; |
|||
if (obj.GetType() != this.GetType()) return false; |
|||
return Equals((TextPosition) obj); |
|||
} |
|||
|
|||
public override int GetHashCode() |
|||
{ |
|||
unchecked |
|||
{ |
|||
return (offset * 397) ^ (int) affinity; |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
Shader "UIWidgets/Text Shader" { |
|||
Properties { |
|||
_MainTex ("Font Texture", 2D) = "white" {} |
|||
_Color ("Text Color", Color) = (1,1,1,1) |
|||
} |
|||
|
|||
SubShader { |
|||
|
|||
Tags { |
|||
"Queue"="Transparent" |
|||
"IgnoreProjector"="True" |
|||
"RenderType"="Transparent" |
|||
"PreviewType"="Plane" |
|||
} |
|||
Lighting Off Cull Off ZTest Always ZWrite Off |
|||
Blend SrcAlpha OneMinusSrcAlpha |
|||
|
|||
Pass { |
|||
CGPROGRAM |
|||
#pragma vertex vert |
|||
#pragma fragment frag |
|||
#pragma multi_compile _ UNITY_SINGLE_PASS_STEREO STEREO_INSTANCING_ON STEREO_MULTIVIEW_ON |
|||
#include "UnityCG.cginc" |
|||
|
|||
struct appdata_t { |
|||
float4 vertex : POSITION; |
|||
fixed4 color : COLOR; |
|||
float2 texcoord : TEXCOORD0; |
|||
UNITY_VERTEX_INPUT_INSTANCE_ID |
|||
}; |
|||
|
|||
struct v2f { |
|||
float4 vertex : SV_POSITION; |
|||
fixed4 color : COLOR; |
|||
float2 texcoord : TEXCOORD0; |
|||
float2 clipUV : TEXCOORD1; |
|||
UNITY_VERTEX_OUTPUT_STEREO |
|||
}; |
|||
|
|||
sampler2D _MainTex; |
|||
uniform float4 _MainTex_ST; |
|||
uniform fixed4 _Color; |
|||
|
|||
#include "UIWidgets_CG.cginc" |
|||
|
|||
v2f vert (appdata_t v) |
|||
{ |
|||
v2f o; |
|||
UNITY_SETUP_INSTANCE_ID(v); |
|||
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); |
|||
o.vertex = UnityObjectToClipPos(v.vertex); |
|||
float3 eyePos = UnityObjectToViewPos(v.vertex); |
|||
o.color = v.color * _Color; |
|||
o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex); |
|||
o.clipUV = mul(UIWidgets_GUIClipMatrix, float4(eyePos.xy, 0, 1.0)); |
|||
return o; |
|||
} |
|||
|
|||
fixed4 frag (v2f i) : SV_Target |
|||
{ |
|||
fixed4 col = i.color; |
|||
col.a *= tex2D(_MainTex, i.texcoord).a; |
|||
float pixelScale = 1.0f / abs(ddx(i.clipUV.x)); |
|||
col.a *= getClipAlpha(i.clipUV, pixelScale); |
|||
return col; |
|||
} |
|||
ENDCG |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 3d9aebae673b434491eb311fc9d0df0f |
|||
timeCreated: 1536033642 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using UIWidgets.editor; |
|||
using UIWidgets.painting; |
|||
using UIWidgets.rendering; |
|||
using UIWidgets.ui; |
|||
using UnityEditor; |
|||
using UnityEngine; |
|||
using Color = UIWidgets.ui.Color; |
|||
using FontStyle = UIWidgets.ui.FontStyle; |
|||
using Rect = UIWidgets.ui.Rect; |
|||
|
|||
namespace UIWidgets.Tests |
|||
{ |
|||
public class Paragraph : EditorWindow |
|||
{ |
|||
private readonly Func<RenderBox>[] _options; |
|||
|
|||
private readonly string[] _optionStrings; |
|||
|
|||
private int _selected; |
|||
|
|||
Paragraph() { |
|||
this._options = new Func<RenderBox>[] { |
|||
this.text, |
|||
this.textHeight, |
|||
this.textOverflow |
|||
}; |
|||
this._optionStrings = this._options.Select(x => x.Method.Name).ToArray(); |
|||
this._selected = 0; |
|||
|
|||
this.titleContent = new GUIContent("RenderBoxes"); |
|||
} |
|||
|
|||
private WindowAdapter windowAdapter; |
|||
|
|||
private RendererBindings rendererBindings; |
|||
|
|||
[NonSerialized] private bool hasInvoked = false; |
|||
|
|||
void OnGUI() { |
|||
var selected = EditorGUILayout.Popup("test case", this._selected, this._optionStrings); |
|||
if (selected != this._selected || !this.hasInvoked) { |
|||
this._selected = selected; |
|||
this.hasInvoked = true; |
|||
|
|||
var renderBox = this._options[this._selected](); |
|||
this.rendererBindings.setRoot(renderBox); |
|||
} |
|||
|
|||
if (this.windowAdapter != null) { |
|||
this.windowAdapter.OnGUI(); |
|||
} |
|||
} |
|||
|
|||
void Update() { |
|||
if (this.windowAdapter != null) { |
|||
this.windowAdapter.Update(); |
|||
} |
|||
} |
|||
|
|||
private void OnEnable() { |
|||
this.windowAdapter = new WindowAdapter(this); |
|||
this.rendererBindings = new RendererBindings(this.windowAdapter); |
|||
} |
|||
|
|||
void OnDestroy() { |
|||
this.windowAdapter = null; |
|||
this.rendererBindings = null; |
|||
} |
|||
|
|||
RenderBox none() { |
|||
return null; |
|||
} |
|||
|
|||
private RenderBox box(RenderParagraph p, int width = 300, int height = 300) |
|||
{ |
|||
return new RenderConstrainedOverflowBox( |
|||
minWidth: width, |
|||
maxWidth: width, |
|||
minHeight: height, |
|||
maxHeight: height, |
|||
alignment: Alignment.center, |
|||
child: new RenderDecoratedBox( |
|||
decoration: new BoxDecoration( |
|||
color: new Color(0xFFFFFFFF), |
|||
borderRadius: BorderRadius.all(3), |
|||
border: Border.all(Color.fromARGB(255, 255, 0, 0), 1) |
|||
), |
|||
child: new RenderPadding(EdgeInsets.all(10), p |
|||
) |
|||
) |
|||
); |
|||
} |
|||
|
|||
RenderBox text() |
|||
{ |
|||
return box( |
|||
new RenderParagraph(new TextSpan("", children: |
|||
new List<TextSpan>() |
|||
{ |
|||
new TextSpan("Real-time 3D revolutioni淡粉色的方式地方zes the animation pipeline ", null), |
|||
new TextSpan(style: new painting.TextStyle(color: Color.fromARGB(255, 255, 0, 0)), |
|||
text: "for Disney Television Animation's “Baymax Dreams"), |
|||
new TextSpan(" Unity Widgets"), |
|||
new TextSpan(" Text"), |
|||
new TextSpan("Real-time 3D revolutionizes the animation pipeline "), |
|||
new TextSpan(style: new painting.TextStyle(color: Color.fromARGB(125, 255, 0, 0)), |
|||
text: "Transparent Red Text\n\n"), |
|||
new TextSpan(style: new painting.TextStyle(fontWeight: FontWeight.w700), |
|||
text: "Bold Text Test Bold Textfs Test: FontWeight.w70\n\n"), |
|||
new TextSpan(style: new painting.TextStyle(fontStyle: FontStyle.italic), |
|||
text: "This is FontStyle.italic Text This is FontStyle.italic Text\n\n"), |
|||
new TextSpan(style: new painting.TextStyle(fontStyle: FontStyle.italic, fontWeight: FontWeight.w700), |
|||
text: "This is FontStyle.italic And 发撒放豆腐sad 发生的 Bold Text This is FontStyle.italic And Bold Text\n\n"), |
|||
new TextSpan(style: new painting.TextStyle(fontSize: 18), |
|||
text: "FontSize 18: Get a named matrix value from the shader."), |
|||
new TextSpan(style: new painting.TextStyle(fontSize: 14), |
|||
text: "FontSize 14"), |
|||
}))); |
|||
} |
|||
|
|||
RenderBox textOverflow() |
|||
{ |
|||
return box( |
|||
new RenderParagraph(new TextSpan("", children: |
|||
new List<TextSpan>() |
|||
{ |
|||
new TextSpan("Real-time 3D revolutionizes:\n the animation pipeline.\n\n\revolutionizesn\n\nReal-time 3D revolutionizes the animation pipeline ", null), |
|||
})), 200, 80); |
|||
} |
|||
|
|||
RenderBox textHeight() |
|||
{ |
|||
var text = |
|||
"Hello UIWidgets. Real-time 3D revolutionize \nReal-time 3D revolutionize\nReal-time 3D revolutionize\n\n"; |
|||
return box( |
|||
new RenderParagraph(new TextSpan(text: "", children: |
|||
new List<TextSpan>() |
|||
{ |
|||
new TextSpan(style: new painting.TextStyle(height: 1), |
|||
text: "Height 1.0 Text:" + text), |
|||
new TextSpan(style: new painting.TextStyle(height: 1.2), |
|||
text: "Height 1.2 Text:" + text), |
|||
new TextSpan(style: new painting.TextStyle(height: 1.5), |
|||
text: "Height 1.5 Text:" + text), |
|||
}))); |
|||
} |
|||
|
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: f4121086094d45de9e29781269270102 |
|||
timeCreated: 1535424100 |
|
|||
fileFormatVersion: 2 |
|||
guid: c53bae489a2f499ea5b9000edb31b0bb |
|||
timeCreated: 1535421229 |
|
|||
using System; |
|||
using System.Runtime.ConstrainedExecution; |
|||
using UIWidgets.math; |
|||
using UIWidgets.ui; |
|||
using UnityEngine; |
|||
using Canvas = UIWidgets.ui.Canvas; |
|||
|
|||
namespace UIWidgets.painting |
|||
{ |
|||
public class TextPainter |
|||
{ |
|||
private TextSpan _text; |
|||
private TextAlign _textAlign; |
|||
private TextDirection? _textDirection; |
|||
private double _textScaleFactor; |
|||
private Paragraph _layoutTemplate; |
|||
private Paragraph _paragraph; |
|||
private bool _needsLayout = true; |
|||
private int _maxLines; |
|||
private string _ellipsis; |
|||
private double _lastMinWidth; |
|||
private double _lastMaxWidth; |
|||
|
|||
public TextPainter(TextSpan text, |
|||
TextAlign textAlign = TextAlign.left, |
|||
TextDirection textDirection = TextDirection.ltr, |
|||
double textScaleFactor = 1.0, |
|||
int maxLines = 0, |
|||
string ellipsis = "") |
|||
{ |
|||
_text = text; |
|||
_textAlign = textAlign; |
|||
_textDirection = textDirection; |
|||
_textScaleFactor = textScaleFactor; |
|||
_maxLines = maxLines; |
|||
_ellipsis = ellipsis; |
|||
} |
|||
|
|||
|
|||
public double textScaleFactor |
|||
{ |
|||
get { return _textScaleFactor; } |
|||
set |
|||
{ |
|||
if (_textScaleFactor == value) |
|||
return; |
|||
_textScaleFactor = value; |
|||
_paragraph = null; |
|||
_layoutTemplate = null; |
|||
_needsLayout = true; |
|||
} |
|||
} |
|||
|
|||
public string ellipsis |
|||
{ |
|||
get { return _ellipsis; } |
|||
set |
|||
{ |
|||
if (_ellipsis == value) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
_ellipsis = value; |
|||
_paragraph = null; |
|||
_needsLayout = true; |
|||
} |
|||
} |
|||
|
|||
public TextSpan text |
|||
{ |
|||
get { return _text; } |
|||
set |
|||
{ |
|||
if (text.Equals(value)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (!Equals(_text == null ? null : _text.style, value == null ? null : value.style)) |
|||
{ |
|||
_layoutTemplate = null; |
|||
} |
|||
|
|||
_text = value; |
|||
_paragraph = null; |
|||
_needsLayout = true; |
|||
} |
|||
} |
|||
|
|||
public Size size |
|||
{ |
|||
get |
|||
{ |
|||
Debug.Assert(!_needsLayout); |
|||
return new Size(width, height); |
|||
} |
|||
} |
|||
|
|||
public TextDirection? textDirection |
|||
{ |
|||
get { return _textDirection; } |
|||
set |
|||
{ |
|||
if (textDirection == value) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
_textDirection = value; |
|||
_paragraph = null; |
|||
_layoutTemplate = null; |
|||
_needsLayout = true; |
|||
} |
|||
} |
|||
|
|||
public TextAlign textAlign |
|||
{ |
|||
get { return _textAlign; } |
|||
set |
|||
{ |
|||
if (_textAlign == value) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
_textAlign = value; |
|||
_paragraph = null; |
|||
_needsLayout = true; |
|||
} |
|||
} |
|||
|
|||
public bool didExceedMaxLines |
|||
{ |
|||
get |
|||
{ |
|||
Debug.Assert(!_needsLayout); |
|||
return _paragraph.didExceedMaxLines; |
|||
} |
|||
} |
|||
|
|||
public int maxLines |
|||
{ |
|||
get { return _maxLines; } |
|||
set |
|||
{ |
|||
if (_maxLines == value) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
_maxLines = value; |
|||
_paragraph = null; |
|||
_needsLayout = true; |
|||
} |
|||
} |
|||
|
|||
public double minIntrinsicWidth |
|||
{ |
|||
get |
|||
{ |
|||
Debug.Assert(!_needsLayout); |
|||
return _applyFloatingPointHack(_paragraph.minIntrinsicWidth); |
|||
} |
|||
} |
|||
|
|||
public double maxIntrinsicWidth |
|||
{ |
|||
get |
|||
{ |
|||
Debug.Assert(!_needsLayout); |
|||
return _applyFloatingPointHack(_paragraph.maxIntrinsicWidth); |
|||
} |
|||
} |
|||
|
|||
public double height |
|||
{ |
|||
get |
|||
{ |
|||
Debug.Assert(!_needsLayout); |
|||
return _applyFloatingPointHack(_paragraph.height); |
|||
} |
|||
} |
|||
|
|||
public double width |
|||
{ |
|||
get |
|||
{ |
|||
Debug.Assert(!_needsLayout); |
|||
return _applyFloatingPointHack(_paragraph.width); |
|||
} |
|||
} |
|||
|
|||
public double computeDistanceToActualBaseline(TextBaseline baseline) |
|||
{ |
|||
Debug.Assert(!_needsLayout); |
|||
switch (baseline) |
|||
{ |
|||
case TextBaseline.alphabetic: |
|||
return _paragraph.alphabeticBaseline; |
|||
case TextBaseline.ideographic: |
|||
return _paragraph.ideographicBaseline; |
|||
} |
|||
return 0.0; |
|||
} |
|||
|
|||
public void layout(double minWidth = 0.0, double maxWidth = double.PositiveInfinity) |
|||
{ |
|||
Debug.Assert(text != null, "TextPainter.text must be set to a non-null value before using the TextPainter."); |
|||
Debug.Assert(textDirection != null, "TextPainter.textDirection must be set to a non-null value before using the TextPainter."); |
|||
if (_needsLayout && minWidth == _lastMaxWidth && maxWidth == _lastMaxWidth) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
_needsLayout = false; |
|||
if (_paragraph == null) |
|||
{ |
|||
var builder = new ParagraphBuilder(_createParagraphStyle()); |
|||
_text.build(builder, textScaleFactor); |
|||
_paragraph = builder.build(); |
|||
} |
|||
|
|||
_lastMinWidth = minWidth; |
|||
_lastMaxWidth = minWidth; |
|||
_paragraph.layout(new ParagraphConstraints(maxWidth)); |
|||
|
|||
if (minWidth != maxWidth) |
|||
{ |
|||
var newWidth = MathUtil.Clamp(maxIntrinsicWidth, minWidth, maxWidth); |
|||
if (newWidth != width) |
|||
{ |
|||
_paragraph.layout(new ParagraphConstraints(newWidth)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void paint(Canvas canvas, Offset offset) |
|||
{ |
|||
Debug.Assert(!_needsLayout); |
|||
_paragraph.paint(canvas, offset.dx, offset.dy); |
|||
} |
|||
|
|||
private ParagraphStyle _createParagraphStyle(TextDirection defaultTextDirection = TextDirection.ltr) |
|||
{ |
|||
if (_text.style == null) |
|||
{ |
|||
return new ParagraphStyle( |
|||
textAlign: textAlign, |
|||
textDirection: textDirection ?? defaultTextDirection, |
|||
maxLines: maxLines, |
|||
ellipsis: ellipsis |
|||
); |
|||
} |
|||
|
|||
return _text.style.getParagraphStyle(textAlign, textDirection ?? defaultTextDirection, |
|||
ellipsis, maxLines, textScaleFactor); |
|||
} |
|||
|
|||
public double preferredLineHeight |
|||
{ |
|||
get |
|||
{ |
|||
if (_layoutTemplate == null) |
|||
{ |
|||
var builder = new ParagraphBuilder( |
|||
_createParagraphStyle(TextDirection.ltr) |
|||
); // direction doesn't matter, text is just a space
|
|||
if (text != null && text.style != null) |
|||
{ |
|||
builder.pushStyle(text.style.getTextStyle(textScaleFactor)); |
|||
} |
|||
|
|||
builder.addText(" "); |
|||
_layoutTemplate = builder.build(); |
|||
_layoutTemplate.layout(new ParagraphConstraints(double.PositiveInfinity)); |
|||
} |
|||
|
|||
return _layoutTemplate.height; |
|||
} |
|||
} |
|||
|
|||
private double _applyFloatingPointHack(double layoutValue) |
|||
{ |
|||
return Math.Ceiling(layoutValue); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 23fd2d1247bf4e308d9f88f3488da38f |
|||
timeCreated: 1535348672 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using UIWidgets.ui; |
|||
using UnityEditor; |
|||
using UnityEngine.Assertions; |
|||
|
|||
namespace UIWidgets.painting |
|||
{ |
|||
public class GestureMock |
|||
{ |
|||
|
|||
} |
|||
public class TextSpan: IEquatable<TextSpan> |
|||
{ |
|||
public delegate bool Visitor(TextSpan span); |
|||
public readonly TextStyle style; |
|||
public readonly string text; |
|||
public readonly List<TextSpan> children; |
|||
public readonly GestureMock recognizer; |
|||
|
|||
public TextSpan(string text = "", TextStyle style = null, List<TextSpan> children = null) |
|||
{ |
|||
this.text = text; |
|||
this.style = style; |
|||
this.children = children; |
|||
} |
|||
|
|||
public void build(ParagraphBuilder builder, double textScaleFactor = 1.0) |
|||
{ |
|||
var hasTyle = style != null; |
|||
if (hasTyle) |
|||
{ |
|||
builder.pushStyle(style.getTextStyle(textScaleFactor)); |
|||
} |
|||
if (!string.IsNullOrEmpty(text)) |
|||
{ |
|||
builder.addText(text); |
|||
} |
|||
if (children != null) |
|||
{ |
|||
foreach (var child in children) |
|||
{ |
|||
Assert.IsNotNull(child); |
|||
child.build(builder, textScaleFactor); |
|||
} |
|||
} |
|||
|
|||
if (hasTyle) |
|||
{ |
|||
builder.pop(); |
|||
} |
|||
} |
|||
|
|||
bool visitTextSpan(Visitor visitor) |
|||
{ |
|||
if (!string.IsNullOrEmpty(text)) |
|||
{ |
|||
if (!visitor.Invoke(this)) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
if (children != null) |
|||
{ |
|||
foreach (var child in children) |
|||
{ |
|||
if (!child.visitTextSpan(visitor)) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
} |
|||
return true; |
|||
} |
|||
|
|||
TextSpan getSpanForPosition(TextPosition position) |
|||
{ |
|||
var offset = 0; |
|||
var targetOffset = position.offset; |
|||
var affinity = position.affinity; |
|||
TextSpan result = null; |
|||
visitTextSpan((span) => |
|||
{ |
|||
var endOffset = offset + span.text.Length; |
|||
if ((targetOffset == offset && affinity == TextAffinity.downstream) || |
|||
(targetOffset > offset && targetOffset < endOffset) || |
|||
(targetOffset == endOffset && affinity == TextAffinity.upstream)) |
|||
{ |
|||
result = span; |
|||
return false; |
|||
} |
|||
|
|||
offset = endOffset; |
|||
return true; |
|||
}); |
|||
return result; |
|||
} |
|||
|
|||
string toPlainText() |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
visitTextSpan((span) => |
|||
{ |
|||
sb.Append(span.text); |
|||
return true; |
|||
}); |
|||
return sb.ToString(); |
|||
} |
|||
|
|||
public int codeUnitAt(int index) |
|||
{ |
|||
if (index < 0) |
|||
{ |
|||
return -1; |
|||
} |
|||
|
|||
var offset = 0; |
|||
var result = -1; |
|||
visitTextSpan(span => |
|||
{ |
|||
if (index - offset < span.text.Length) |
|||
{ |
|||
result = span.text[index - offset]; |
|||
return false; |
|||
} |
|||
|
|||
offset += span.text.Length; |
|||
return true; |
|||
}); |
|||
return result; |
|||
} |
|||
|
|||
public RenderComparison compareTo(TextSpan other) |
|||
{ |
|||
if (Equals(other)) |
|||
{ |
|||
return RenderComparison.identical; |
|||
} |
|||
|
|||
if (other.text != text |
|||
|| ((children == null) != (other.children == null)) |
|||
|| (children != null && other.children != null && children.Count != other.children.Count) |
|||
|| ((style == null) != (other.style != null)) |
|||
) |
|||
{ |
|||
return RenderComparison.layout; |
|||
} |
|||
|
|||
RenderComparison result = Equals(recognizer, other.recognizer) |
|||
? RenderComparison.identical |
|||
: RenderComparison.metadata; |
|||
if (style != null) |
|||
{ |
|||
var candidate = style.compareTo(other.style); |
|||
if (candidate > result) |
|||
{ |
|||
result = candidate; |
|||
} |
|||
|
|||
if (result == RenderComparison.layout) |
|||
{ |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
if (children != null) |
|||
{ |
|||
for (var index = 0; index < children.Count; index++) |
|||
{ |
|||
var candidate = children[index].compareTo(other.children[index]); |
|||
if (candidate > result) |
|||
{ |
|||
result = candidate; |
|||
} |
|||
if (result == RenderComparison.layout) |
|||
{ |
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (ReferenceEquals(null, obj)) return false; |
|||
if (ReferenceEquals(this, obj)) return true; |
|||
if (obj.GetType() != this.GetType()) return false; |
|||
return Equals((TextSpan) obj); |
|||
} |
|||
|
|||
public override int GetHashCode() |
|||
{ |
|||
unchecked |
|||
{ |
|||
var hashCode = (style != null ? style.GetHashCode() : 0); |
|||
hashCode = (hashCode * 397) ^ (text != null ? text.GetHashCode() : 0); |
|||
hashCode = (hashCode * 397) ^ (childHash()); |
|||
return hashCode; |
|||
} |
|||
} |
|||
|
|||
public bool Equals(TextSpan other) |
|||
{ |
|||
if (ReferenceEquals(null, other)) return false; |
|||
if (ReferenceEquals(this, other)) return true; |
|||
return Equals(style, other.style) && string.Equals(text, other.text) && childEquals(children, other.children); |
|||
} |
|||
|
|||
private int childHash() |
|||
{ |
|||
unchecked |
|||
{ |
|||
var hashCode = 0; |
|||
if (children != null) |
|||
{ |
|||
foreach (var child in children) |
|||
{ |
|||
hashCode = (hashCode * 397) ^ (child != null ? child.GetHashCode() : 0); |
|||
} |
|||
} |
|||
|
|||
return hashCode; |
|||
} |
|||
} |
|||
|
|||
private static bool childEquals(List<TextSpan> left, List<TextSpan> right) |
|||
{ |
|||
if (ReferenceEquals(left, right)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
if (left == null || right == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return left.SequenceEqual(right); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 08861f205e19466880d9af4e7cc1dde4 |
|||
timeCreated: 1535348576 |
|
|||
using System; |
|||
using UIWidgets.painting; |
|||
using UIWidgets.ui; |
|||
|
|||
namespace UIWidgets.painting |
|||
{ |
|||
public class TextStyle |
|||
{ |
|||
public static readonly double _defaultFontSize = 14.0; |
|||
public readonly bool? inherit; |
|||
public readonly Color color; |
|||
public readonly double? fontSize; |
|||
public readonly FontWeight? fontWeight; |
|||
public readonly FontStyle? fontStyle; |
|||
public readonly double? letterSpacing; |
|||
public readonly double? wordSpacing; |
|||
public readonly TextBaseline? textBaseline; |
|||
public readonly double? height; |
|||
public readonly TextDecoration decoration; |
|||
public readonly string fontFamily; |
|||
|
|||
public TextStyle(bool? inherit = null, Color color = null, double? fontSize = null, FontWeight? fontWeight = null, |
|||
FontStyle? fontStyle = null, double? letterSpacing = null, double? wordSpacing = null, |
|||
TextBaseline? textBaseline = null, double? height = null, TextDecoration decoration = null, string fontFamily = null) |
|||
{ |
|||
this.inherit = inherit; |
|||
this.color = color; |
|||
this.fontSize = fontSize; |
|||
this.fontWeight = fontWeight; |
|||
this.fontStyle = fontStyle; |
|||
this.letterSpacing = letterSpacing; |
|||
this.wordSpacing = wordSpacing; |
|||
this.textBaseline = textBaseline; |
|||
this.height = height; |
|||
this.decoration = decoration; |
|||
this.fontFamily = fontFamily; |
|||
} |
|||
|
|||
public ui.TextStyle getTextStyle(double textScaleFactor = 1.0) |
|||
{ |
|||
return new ui.TextStyle( |
|||
color: color, |
|||
decoration: decoration, |
|||
fontWeight: fontWeight, |
|||
fontStyle: fontStyle, |
|||
fontSize: fontSize == null ? null : fontSize * textScaleFactor, |
|||
letterSpacing: letterSpacing, |
|||
wordSpacing: wordSpacing, |
|||
textBaseline: textBaseline, |
|||
height: height, |
|||
fontFamily: fontFamily |
|||
); |
|||
} |
|||
|
|||
public RenderComparison compareTo(TextStyle other) |
|||
{ |
|||
if (inherit != other.inherit || fontFamily != other.fontFamily |
|||
|| fontSize != other.fontSize || fontWeight != other.fontWeight |
|||
|| fontStyle != other.fontStyle || letterSpacing != other.letterSpacing |
|||
|| wordSpacing != other.wordSpacing || textBaseline != other.textBaseline |
|||
|| height != other.height) |
|||
{ |
|||
return RenderComparison.layout; |
|||
} |
|||
|
|||
if (color != other.color || decoration != other.decoration) |
|||
{ |
|||
return RenderComparison.paint; |
|||
} |
|||
return RenderComparison.identical; |
|||
} |
|||
|
|||
public ParagraphStyle getParagraphStyle(TextAlign textAlign, |
|||
TextDirection textDirection, string ellipsis, int maxLines, double textScaleFactor = 1.0) |
|||
{ |
|||
|
|||
return new ParagraphStyle( |
|||
textAlign, textDirection, fontWeight, fontStyle, |
|||
maxLines, (fontSize ?? _defaultFontSize) * textScaleFactor, |
|||
fontFamily, height, ellipsis |
|||
); |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 93d2ea584e0947e1b61a8ca8bdd1cf9a |
|||
timeCreated: 1535348621 |
|
|||
using System; |
|||
using UIWidgets.painting; |
|||
using UIWidgets.ui; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgets.rendering |
|||
{ |
|||
public enum TextOverflow { |
|||
/// Clip the overflowing text to fix its container.
|
|||
clip, |
|||
|
|||
/// Fade the overflowing text to transparent.
|
|||
fade, |
|||
|
|||
/// Use an ellipsis to indicate that the text has overflowed.
|
|||
ellipsis, |
|||
} |
|||
|
|||
|
|||
public class RenderParagraph: RenderBox |
|||
{ |
|||
|
|||
private static readonly string _kEllipsis = "\u2026"; |
|||
|
|||
private bool _softWrap; |
|||
|
|||
private TextOverflow _overflow; |
|||
private readonly TextPainter _textPainter; |
|||
private bool _hasVisualOverflow = false; |
|||
|
|||
public RenderParagraph(TextSpan text, |
|||
TextAlign textAlign = TextAlign.left, |
|||
TextDirection textDirection = TextDirection.ltr, |
|||
bool softWrap = true, |
|||
TextOverflow overflow = TextOverflow.clip, |
|||
double textScaleFactor = 1.0, |
|||
int maxLines = 0 |
|||
) |
|||
{ |
|||
_softWrap = softWrap; |
|||
_overflow = overflow; |
|||
_textPainter = new TextPainter( |
|||
text, |
|||
textAlign, |
|||
textDirection, |
|||
textScaleFactor, |
|||
maxLines, |
|||
overflow == TextOverflow.ellipsis ? _kEllipsis : "" |
|||
); |
|||
} |
|||
|
|||
public TextSpan text |
|||
{ |
|||
get |
|||
{ |
|||
return _textPainter.text; |
|||
} |
|||
|
|||
set |
|||
{ |
|||
Debug.Assert(value != null); |
|||
switch (_textPainter.text.compareTo(value)) |
|||
{ |
|||
case RenderComparison.identical: |
|||
case RenderComparison.metadata: |
|||
return; |
|||
case RenderComparison.paint: |
|||
_textPainter.text = value; |
|||
markNeedsPaint(); |
|||
break; |
|||
case RenderComparison.layout: |
|||
_textPainter.text = value; |
|||
markNeedsLayout(); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public TextAlign textAlign |
|||
{ |
|||
get { return _textPainter.textAlign; } |
|||
set |
|||
{ |
|||
if (_textPainter.textAlign == value) |
|||
{ |
|||
return; |
|||
} |
|||
_textPainter.textAlign = value; |
|||
markNeedsPaint(); |
|||
} |
|||
} |
|||
|
|||
public TextDirection? textDirection |
|||
{ |
|||
get { return _textPainter.textDirection; } |
|||
set |
|||
{ |
|||
if (_textPainter.textDirection == value) |
|||
{ |
|||
return; |
|||
} |
|||
_textPainter.textDirection = textDirection; |
|||
markNeedsLayout(); |
|||
} |
|||
} |
|||
|
|||
public bool softWrap |
|||
{ |
|||
get { return _softWrap; } |
|||
set |
|||
{ |
|||
if (_softWrap == value) |
|||
{ |
|||
return; |
|||
} |
|||
_softWrap = value; |
|||
markNeedsLayout(); |
|||
} |
|||
} |
|||
|
|||
public TextOverflow overflow |
|||
{ |
|||
get { return _overflow; } |
|||
set |
|||
{ |
|||
if (_overflow == value) |
|||
{ |
|||
return; |
|||
} |
|||
_overflow = value; |
|||
_textPainter.ellipsis = value == TextOverflow.ellipsis ? _kEllipsis : null; |
|||
// _textPainter.e
|
|||
markNeedsLayout(); |
|||
} |
|||
} |
|||
|
|||
public double textScaleFactor |
|||
{ |
|||
get { return _textPainter.textScaleFactor; } |
|||
set |
|||
{ |
|||
if (Math.Abs(_textPainter.textScaleFactor - value) < 0.00000001) |
|||
{ |
|||
return; |
|||
} |
|||
_textPainter.textScaleFactor = value; |
|||
markNeedsLayout(); |
|||
} |
|||
} |
|||
|
|||
public int maxLines |
|||
{ |
|||
get { return _textPainter.maxLines; } |
|||
set |
|||
{ |
|||
if (_textPainter.maxLines == value) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
_textPainter.maxLines = value; |
|||
markNeedsLayout(); |
|||
} |
|||
} |
|||
|
|||
public Size textSize |
|||
{ |
|||
get { return _textPainter.size; } |
|||
} |
|||
|
|||
public override double computeMinIntrinsicWidth(double height) { |
|||
_layoutText(); |
|||
return _textPainter.minIntrinsicWidth; |
|||
} |
|||
|
|||
public override double computeMaxIntrinsicWidth(double height) { |
|||
_layoutText(); |
|||
return _textPainter.maxIntrinsicWidth; |
|||
} |
|||
|
|||
double _computeIntrinsicHeight(double width) { |
|||
_layoutText(minWidth: width, maxWidth: width); |
|||
return _textPainter.height; |
|||
} |
|||
|
|||
public override double computeMinIntrinsicHeight(double width) { |
|||
return _computeIntrinsicHeight(width); |
|||
} |
|||
|
|||
public override double computeMaxIntrinsicHeight(double width) { |
|||
return _computeIntrinsicHeight(width); |
|||
} |
|||
|
|||
public override double? computeDistanceToActualBaseline(TextBaseline baseline) { |
|||
_layoutTextWithConstraints(constraints); |
|||
return _textPainter.computeDistanceToActualBaseline(baseline); |
|||
} |
|||
|
|||
|
|||
public override void performLayout() { |
|||
_layoutTextWithConstraints(constraints); |
|||
var textSize = _textPainter.size; |
|||
var didOverflowHeight = _textPainter.didExceedMaxLines; |
|||
size = constraints.constrain(textSize); |
|||
|
|||
var didOverflowWidth = size.width < textSize.width; |
|||
_hasVisualOverflow = didOverflowWidth || didOverflowHeight; |
|||
} |
|||
|
|||
public override void paint(PaintingContext context, Offset offset) { |
|||
_layoutTextWithConstraints(constraints); |
|||
var canvas = context.canvas; |
|||
|
|||
if (_hasVisualOverflow) { |
|||
var bounds = offset & size; |
|||
canvas.save(); |
|||
canvas.clipRect(bounds); |
|||
} |
|||
_textPainter.paint(canvas, offset); |
|||
if (_hasVisualOverflow) { |
|||
canvas.restore(); |
|||
} |
|||
} |
|||
|
|||
private void _layoutText(double minWidth = 0.0, double maxWidth = double.PositiveInfinity) |
|||
{ |
|||
var widthMatters = softWrap || overflow == TextOverflow.ellipsis; |
|||
_textPainter.layout(minWidth, widthMatters ? maxWidth : double.PositiveInfinity); |
|||
} |
|||
|
|||
private void _layoutTextWithConstraints(BoxConstraints constraints) { |
|||
_layoutText(minWidth: constraints.minWidth, maxWidth: constraints.maxWidth); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 69e1c7c86f0d4f4e9aae4c3131653270 |
|||
timeCreated: 1535348727 |
|
|||
using System; |
|||
using UIWidgets.ui; |
|||
|
|||
namespace UIWidgets.rendering { |
|||
public abstract class RenderSliverFixedExtentBoxAdaptor : RenderSliverMultiBoxAdaptor { |
|||
RenderSliverFixedExtentBoxAdaptor( |
|||
RenderSliverBoxChildManager childManager = null |
|||
) : base(childManager: childManager) { |
|||
} |
|||
|
|||
public abstract double itemExtent { get; } |
|||
|
|||
public double indexToLayoutOffset(double itemExtent, int index) { |
|||
return itemExtent * index; |
|||
} |
|||
|
|||
public int getMinChildIndexForScrollOffset(double scrollOffset, double itemExtent) { |
|||
return itemExtent > 0.0 ? Math.Max(0, (int) (scrollOffset / itemExtent)) : 0; |
|||
} |
|||
|
|||
public int getMaxChildIndexForScrollOffset(double scrollOffset, double itemExtent) { |
|||
return itemExtent > 0.0 ? Math.Max(0, (int) Math.Ceiling(scrollOffset / itemExtent) - 1) : 0; |
|||
} |
|||
|
|||
public double estimateMaxScrollOffset(SliverConstraints constraints, |
|||
int firstIndex = 0, |
|||
int lastIndex = 0, |
|||
double leadingScrollOffset = 0.0, |
|||
double trailingScrollOffset = 0.0 |
|||
) { |
|||
return this.childManager.estimateMaxScrollOffset( |
|||
constraints, |
|||
firstIndex: firstIndex, |
|||
lastIndex: lastIndex, |
|||
leadingScrollOffset: leadingScrollOffset, |
|||
trailingScrollOffset: trailingScrollOffset |
|||
); |
|||
} |
|||
|
|||
public double computeMaxScrollOffset(SliverConstraints constraints, double itemExtent) { |
|||
return this.childManager.childCount * itemExtent; |
|||
} |
|||
|
|||
|
|||
public override void performLayout() { |
|||
this.childManager.didStartLayout(); |
|||
this.childManager.setDidUnderflow(false); |
|||
|
|||
double itemExtent = this.itemExtent; |
|||
|
|||
double scrollOffset = this.constraints.scrollOffset + this.constraints.cacheOrigin; |
|||
double remainingExtent = this.constraints.remainingCacheExtent; |
|||
double targetEndScrollOffset = scrollOffset + remainingExtent; |
|||
|
|||
BoxConstraints childConstraints = this.constraints.asBoxConstraints( |
|||
minExtent: itemExtent, |
|||
maxExtent: itemExtent |
|||
); |
|||
|
|||
int firstIndex = this.getMinChildIndexForScrollOffset(scrollOffset, itemExtent); |
|||
int? targetLastIndex = !double.IsInfinity(targetEndScrollOffset) |
|||
? this.getMaxChildIndexForScrollOffset(targetEndScrollOffset, itemExtent) |
|||
: (int?) null; |
|||
|
|||
if (this.firstChild != null) { |
|||
int oldFirstIndex = this.indexOf(this.firstChild); |
|||
int oldLastIndex = this.indexOf(this.lastChild); |
|||
int leadingGarbage = (firstIndex - oldFirstIndex).clamp(0, this.childCount); |
|||
int trailingGarbage = |
|||
targetLastIndex == null ? 0 : (oldLastIndex - targetLastIndex.Value).clamp(0, this.childCount); |
|||
this.collectGarbage(leadingGarbage, trailingGarbage); |
|||
} else { |
|||
this.collectGarbage(0, 0); |
|||
} |
|||
|
|||
if (this.firstChild == null) { |
|||
if (!this.addInitialChild(index: firstIndex, |
|||
layoutOffset: this.indexToLayoutOffset(itemExtent, firstIndex))) { |
|||
double max = this.computeMaxScrollOffset(this.constraints, itemExtent); |
|||
this.geometry = new SliverGeometry( |
|||
scrollExtent: max, |
|||
maxPaintExtent: max |
|||
); |
|||
this.childManager.didFinishLayout(); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
RenderBox trailingChildWithLayout = null; |
|||
|
|||
for (int index = this.indexOf(this.firstChild) - 1; index >= firstIndex; --index) { |
|||
RenderBox child = this.insertAndLayoutLeadingChild(childConstraints); |
|||
if (child == null) { |
|||
this.geometry = new SliverGeometry(scrollOffsetCorrection: index * itemExtent); |
|||
return; |
|||
} |
|||
|
|||
var childParentData = (SliverMultiBoxAdaptorParentData) child.parentData; |
|||
childParentData.layoutOffset = this.indexToLayoutOffset(itemExtent, index); |
|||
trailingChildWithLayout = trailingChildWithLayout ?? child; |
|||
} |
|||
|
|||
if (trailingChildWithLayout == null) { |
|||
this.firstChild.layout(childConstraints); |
|||
var childParentData = (SliverMultiBoxAdaptorParentData) this.firstChild.parentData; |
|||
childParentData.layoutOffset = this.indexToLayoutOffset(itemExtent, firstIndex); |
|||
trailingChildWithLayout = this.firstChild; |
|||
} |
|||
|
|||
while (targetLastIndex == null || this.indexOf(trailingChildWithLayout) < targetLastIndex) { |
|||
RenderBox child = this.childAfter(trailingChildWithLayout); |
|||
if (child == null) { |
|||
child = this.insertAndLayoutChild(childConstraints, after: trailingChildWithLayout); |
|||
if (child == null) { |
|||
break; |
|||
} |
|||
} else { |
|||
child.layout(childConstraints); |
|||
} |
|||
|
|||
trailingChildWithLayout = child; |
|||
var childParentData = (SliverMultiBoxAdaptorParentData) child.parentData; |
|||
childParentData.layoutOffset = this.indexToLayoutOffset(itemExtent, childParentData.index); |
|||
} |
|||
|
|||
int lastIndex = this.indexOf(this.lastChild); |
|||
double leadingScrollOffset = this.indexToLayoutOffset(itemExtent, firstIndex); |
|||
double trailingScrollOffset = this.indexToLayoutOffset(itemExtent, lastIndex + 1); |
|||
|
|||
double estimatedMaxScrollOffset = this.estimateMaxScrollOffset( |
|||
this.constraints, |
|||
firstIndex: firstIndex, |
|||
lastIndex: lastIndex, |
|||
leadingScrollOffset: leadingScrollOffset, |
|||
trailingScrollOffset: trailingScrollOffset |
|||
); |
|||
|
|||
double paintExtent = this.calculatePaintOffset( |
|||
this.constraints, |
|||
from: leadingScrollOffset, |
|||
to: trailingScrollOffset |
|||
); |
|||
|
|||
double cacheExtent = this.calculateCacheOffset( |
|||
this.constraints, |
|||
from: leadingScrollOffset, |
|||
to: trailingScrollOffset |
|||
); |
|||
|
|||
double targetEndScrollOffsetForPaint = |
|||
this.constraints.scrollOffset + this.constraints.remainingPaintExtent; |
|||
int? targetLastIndexForPaint = !double.IsInfinity(targetEndScrollOffsetForPaint) |
|||
? this.getMaxChildIndexForScrollOffset(targetEndScrollOffsetForPaint, itemExtent) |
|||
: (int?) null; |
|||
this.geometry = new SliverGeometry( |
|||
scrollExtent: estimatedMaxScrollOffset, |
|||
paintExtent: paintExtent, |
|||
cacheExtent: cacheExtent, |
|||
maxPaintExtent: estimatedMaxScrollOffset, |
|||
hasVisualOverflow: (targetLastIndexForPaint != null && lastIndex >= targetLastIndexForPaint) |
|||
|| this.constraints.scrollOffset > 0.0 |
|||
); |
|||
if (estimatedMaxScrollOffset == trailingScrollOffset) { |
|||
this.childManager.setDidUnderflow(true); |
|||
} |
|||
|
|||
this.childManager.didFinishLayout(); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: e602742e1e9c408989efd79055a9e552 |
|||
timeCreated: 1535524936 |
|
|||
using System; |
|||
|
|||
namespace UIWidgets.rendering { |
|||
public class RenderSliverList : RenderSliverMultiBoxAdaptor { |
|||
RenderSliverList( |
|||
RenderSliverBoxChildManager childManager = null |
|||
) : base(childManager: childManager) { |
|||
} |
|||
|
|||
public override void performLayout() { |
|||
this.childManager.didStartLayout(); |
|||
this.childManager.setDidUnderflow(false); |
|||
|
|||
double scrollOffset = this.constraints.scrollOffset + this.constraints.cacheOrigin; |
|||
double remainingExtent = this.constraints.remainingCacheExtent; |
|||
double targetEndScrollOffset = scrollOffset + remainingExtent; |
|||
BoxConstraints childConstraints = this.constraints.asBoxConstraints(); |
|||
int leadingGarbage = 0; |
|||
int trailingGarbage = 0; |
|||
bool reachedEnd = false; |
|||
|
|||
if (this.firstChild == null) { |
|||
if (!this.addInitialChild()) { |
|||
this.geometry = SliverGeometry.zero; |
|||
this.childManager.didFinishLayout(); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
RenderBox leadingChildWithLayout = null, trailingChildWithLayout = null; |
|||
|
|||
RenderBox earliestUsefulChild = this.firstChild; |
|||
for (double earliestScrollOffset = this.childScrollOffset(earliestUsefulChild); |
|||
earliestScrollOffset > scrollOffset; |
|||
earliestScrollOffset = this.childScrollOffset(earliestUsefulChild)) { |
|||
earliestUsefulChild = this.insertAndLayoutLeadingChild(childConstraints, parentUsesSize: true); |
|||
|
|||
if (earliestUsefulChild == null) { |
|||
var childParentData = (SliverMultiBoxAdaptorParentData) this.firstChild.parentData; |
|||
childParentData.layoutOffset = 0.0; |
|||
|
|||
if (scrollOffset == 0.0) { |
|||
earliestUsefulChild = this.firstChild; |
|||
leadingChildWithLayout = earliestUsefulChild; |
|||
trailingChildWithLayout = trailingChildWithLayout ?? earliestUsefulChild; |
|||
break; |
|||
} else { |
|||
this.geometry = new SliverGeometry( |
|||
scrollOffsetCorrection: -scrollOffset |
|||
); |
|||
return; |
|||
} |
|||
} else { |
|||
double firstChildScrollOffset = earliestScrollOffset - this.paintExtentOf(this.firstChild); |
|||
if (firstChildScrollOffset < 0.0) { |
|||
double correction = 0.0; |
|||
while (earliestUsefulChild != null) { |
|||
correction += this.paintExtentOf(firstChild); |
|||
earliestUsefulChild = |
|||
this.insertAndLayoutLeadingChild(childConstraints, parentUsesSize: true); |
|||
} |
|||
|
|||
this.geometry = new SliverGeometry( |
|||
scrollOffsetCorrection: correction - earliestScrollOffset |
|||
); |
|||
var childParentData = (SliverMultiBoxAdaptorParentData) this.firstChild.parentData; |
|||
childParentData.layoutOffset = 0.0; |
|||
return; |
|||
} else { |
|||
var childParentData = (SliverMultiBoxAdaptorParentData) earliestUsefulChild.parentData; |
|||
childParentData.layoutOffset = firstChildScrollOffset; |
|||
leadingChildWithLayout = earliestUsefulChild; |
|||
trailingChildWithLayout = trailingChildWithLayout ?? earliestUsefulChild; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (leadingChildWithLayout == null) { |
|||
earliestUsefulChild.layout(childConstraints, parentUsesSize: true); |
|||
leadingChildWithLayout = earliestUsefulChild; |
|||
trailingChildWithLayout = earliestUsefulChild; |
|||
} |
|||
|
|||
bool inLayoutRange = true; |
|||
RenderBox child = earliestUsefulChild; |
|||
int index = this.indexOf(child); |
|||
double endScrollOffset = this.childScrollOffset(child) + this.paintExtentOf(child); |
|||
|
|||
Func<bool> advance = () => { |
|||
if (child == trailingChildWithLayout) { |
|||
inLayoutRange = false; |
|||
} |
|||
|
|||
child = this.childAfter(child); |
|||
if (child == null) { |
|||
inLayoutRange = false; |
|||
} |
|||
|
|||
index += 1; |
|||
if (!inLayoutRange) { |
|||
if (child == null || this.indexOf(child) != index) { |
|||
child = insertAndLayoutChild(childConstraints, |
|||
after: trailingChildWithLayout, |
|||
parentUsesSize: true |
|||
); |
|||
if (child == null) { |
|||
return false; |
|||
} |
|||
} else { |
|||
child.layout(childConstraints, parentUsesSize: true); |
|||
} |
|||
|
|||
trailingChildWithLayout = child; |
|||
} |
|||
|
|||
var childParentData = (SliverMultiBoxAdaptorParentData) child.parentData; |
|||
childParentData.layoutOffset = endScrollOffset; |
|||
endScrollOffset = this.childScrollOffset(child) + this.paintExtentOf(child); |
|||
return true; |
|||
}; |
|||
|
|||
while (endScrollOffset < scrollOffset) { |
|||
leadingGarbage += 1; |
|||
if (!advance()) { |
|||
this.collectGarbage(leadingGarbage - 1, 0); |
|||
double extent = this.childScrollOffset(this.lastChild) + this.paintExtentOf(this.lastChild); |
|||
this.geometry = new SliverGeometry( |
|||
scrollExtent: extent, |
|||
paintExtent: 0.0, |
|||
maxPaintExtent: extent |
|||
); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
while (endScrollOffset < targetEndScrollOffset) { |
|||
if (!advance()) { |
|||
reachedEnd = true; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (child != null) { |
|||
child = this.childAfter(child); |
|||
while (child != null) { |
|||
trailingGarbage += 1; |
|||
child = this.childAfter(child); |
|||
} |
|||
} |
|||
|
|||
this.collectGarbage(leadingGarbage, trailingGarbage); |
|||
|
|||
double estimatedMaxScrollOffset; |
|||
if (reachedEnd) { |
|||
estimatedMaxScrollOffset = endScrollOffset; |
|||
} else { |
|||
estimatedMaxScrollOffset = this.childManager.estimateMaxScrollOffset( |
|||
this.constraints, |
|||
firstIndex: this.indexOf(this.firstChild), |
|||
lastIndex: this.indexOf(this.lastChild), |
|||
leadingScrollOffset: this.childScrollOffset(this.firstChild), |
|||
trailingScrollOffset: endScrollOffset |
|||
); |
|||
} |
|||
|
|||
double paintExtent = this.calculatePaintOffset( |
|||
this.constraints, |
|||
from: this.childScrollOffset(this.firstChild), |
|||
to: endScrollOffset |
|||
); |
|||
double cacheExtent = this.calculateCacheOffset( |
|||
this.constraints, |
|||
from: this.childScrollOffset(this.firstChild), |
|||
to: endScrollOffset |
|||
); |
|||
double targetEndScrollOffsetForPaint = |
|||
this.constraints.scrollOffset + this.constraints.remainingPaintExtent; |
|||
this.geometry = new SliverGeometry( |
|||
scrollExtent: estimatedMaxScrollOffset, |
|||
paintExtent: paintExtent, |
|||
cacheExtent: cacheExtent, |
|||
maxPaintExtent: estimatedMaxScrollOffset, |
|||
hasVisualOverflow: endScrollOffset > targetEndScrollOffsetForPaint || |
|||
this.constraints.scrollOffset > 0.0 |
|||
); |
|||
|
|||
if (estimatedMaxScrollOffset == endScrollOffset) { |
|||
this.childManager.setDidUnderflow(true); |
|||
} |
|||
|
|||
this.childManager.didFinishLayout(); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 1db4d92862744fcdb527f4020106fdf8 |
|||
timeCreated: 1535522746 |
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using UIWidgets.foundation; |
|||
using UIWidgets.painting; |
|||
using UIWidgets.ui; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgets.rendering { |
|||
public interface RenderSliverBoxChildManager { |
|||
void createChild(int index, RenderBox after = null); |
|||
|
|||
void removeChild(RenderBox child); |
|||
|
|||
double estimateMaxScrollOffset( |
|||
SliverConstraints constraints, |
|||
int firstIndex = 0, |
|||
int lastIndex = 0, |
|||
double leadingScrollOffset = 0, |
|||
double trailingScrollOffset = 0); |
|||
|
|||
int childCount { get; } |
|||
|
|||
void didAdoptChild(RenderBox child); |
|||
|
|||
void setDidUnderflow(bool value); |
|||
|
|||
void didStartLayout(); |
|||
|
|||
void didFinishLayout(); |
|||
} |
|||
|
|||
public class SliverMultiBoxAdaptorParentData : ContainerParentDataMixinSliverLogicalParentData<RenderBox> { |
|||
public int index; |
|||
|
|||
public bool keepAlive = false; |
|||
|
|||
public bool _keptAlive = false; |
|||
} |
|||
|
|||
public abstract class RenderSliverMultiBoxAdaptor |
|||
: ContainerRenderObjectMixinRenderSliver<RenderBox, SliverMultiBoxAdaptorParentData> { |
|||
public RenderSliverMultiBoxAdaptor( |
|||
RenderSliverBoxChildManager childManager = null |
|||
) { |
|||
this._childManager = childManager; |
|||
} |
|||
|
|||
public override void setupParentData(RenderObject child) { |
|||
if (!(child.parentData is SliverMultiBoxAdaptorParentData)) { |
|||
child.parentData = new SliverMultiBoxAdaptorParentData(); |
|||
} |
|||
} |
|||
|
|||
public RenderSliverBoxChildManager childManager { |
|||
get { return this._childManager; } |
|||
} |
|||
|
|||
public RenderSliverBoxChildManager _childManager; |
|||
|
|||
public readonly Dictionary<int, RenderBox> _keepAliveBucket = new Dictionary<int, RenderBox>(); |
|||
|
|||
public override void adoptChild(AbstractNode childNode) { |
|||
base.adoptChild(childNode); |
|||
var child = (RenderBox) childNode; |
|||
var childParentData = (SliverMultiBoxAdaptorParentData) child.parentData; |
|||
if (!childParentData._keptAlive) { |
|||
this.childManager.didAdoptChild(child); |
|||
} |
|||
} |
|||
|
|||
public override void insert(RenderBox child, RenderBox after = null) { |
|||
base.insert(child, after: after); |
|||
} |
|||
|
|||
public override void remove(RenderBox child) { |
|||
var childParentData = (SliverMultiBoxAdaptorParentData) child.parentData; |
|||
if (!childParentData._keptAlive) { |
|||
base.remove(child); |
|||
return; |
|||
} |
|||
|
|||
this._keepAliveBucket.Remove(childParentData.index); |
|||
this.dropChild(child); |
|||
} |
|||
|
|||
public override void removeAll() { |
|||
base.removeAll(); |
|||
|
|||
foreach (var child in this._keepAliveBucket.Values) { |
|||
this.dropChild(child); |
|||
} |
|||
|
|||
this._keepAliveBucket.Clear(); |
|||
} |
|||
|
|||
void _createOrObtainChild(int index, RenderBox after = null) { |
|||
this.invokeLayoutCallback<SliverConstraints>((SliverConstraints constraints) => { |
|||
if (this._keepAliveBucket.ContainsKey(index)) { |
|||
RenderBox child = this._keepAliveBucket[index]; |
|||
this._keepAliveBucket.Remove(index); |
|||
var childParentData = (SliverMultiBoxAdaptorParentData) child.parentData; |
|||
this.dropChild(child); |
|||
child.parentData = childParentData; |
|||
this.insert(child, after: after); |
|||
childParentData._keptAlive = false; |
|||
} else { |
|||
this._childManager.createChild(index, after: after); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public void _destroyOrCacheChild(RenderBox child) { |
|||
var childParentData = (SliverMultiBoxAdaptorParentData) child.parentData; |
|||
if (childParentData.keepAlive) { |
|||
this.remove(child); |
|||
this._keepAliveBucket[childParentData.index] = child; |
|||
child.parentData = childParentData; |
|||
base.adoptChild(child); |
|||
childParentData._keptAlive = true; |
|||
} else { |
|||
this._childManager.removeChild(child); |
|||
} |
|||
} |
|||
|
|||
public override void attach(object owner) { |
|||
base.attach(owner); |
|||
foreach (RenderBox child in this._keepAliveBucket.Values) { |
|||
child.attach(owner); |
|||
} |
|||
} |
|||
|
|||
public override void detach() { |
|||
base.detach(); |
|||
foreach (RenderBox child in this._keepAliveBucket.Values) { |
|||
child.detach(); |
|||
} |
|||
} |
|||
|
|||
public override void redepthChildren() { |
|||
base.redepthChildren(); |
|||
foreach (var child in this._keepAliveBucket.Values) { |
|||
this.redepthChild(child); |
|||
} |
|||
} |
|||
|
|||
public override void visitChildren(RenderObjectVisitor visitor) { |
|||
base.visitChildren(visitor); |
|||
|
|||
foreach (var child in this._keepAliveBucket.Values) { |
|||
visitor(child); |
|||
} |
|||
} |
|||
|
|||
public bool addInitialChild(int index = 0, double layoutOffset = 0.0) { |
|||
this._createOrObtainChild(index, after: null); |
|||
if (this.firstChild != null) { |
|||
var firstChildParentData = (SliverMultiBoxAdaptorParentData) this.firstChild.parentData; |
|||
firstChildParentData.layoutOffset = layoutOffset; |
|||
return true; |
|||
} |
|||
|
|||
this.childManager.setDidUnderflow(true); |
|||
return false; |
|||
} |
|||
|
|||
public RenderBox insertAndLayoutLeadingChild(BoxConstraints childConstraints, bool parentUsesSize = false) { |
|||
int index = this.indexOf(this.firstChild) - 1; |
|||
this._createOrObtainChild(index, after: null); |
|||
if (this.indexOf(this.firstChild) == index) { |
|||
this.firstChild.layout(childConstraints, parentUsesSize: parentUsesSize); |
|||
return this.firstChild; |
|||
} |
|||
|
|||
this.childManager.setDidUnderflow(true); |
|||
return null; |
|||
} |
|||
|
|||
public RenderBox insertAndLayoutChild( |
|||
BoxConstraints childConstraints, |
|||
RenderBox after = null, |
|||
bool parentUsesSize = false |
|||
) { |
|||
int index = this.indexOf(after) + 1; |
|||
this._createOrObtainChild(index, after: after); |
|||
RenderBox child = this.childAfter(after); |
|||
if (child != null && this.indexOf(child) == index) { |
|||
child.layout(childConstraints, parentUsesSize: parentUsesSize); |
|||
return child; |
|||
} |
|||
|
|||
this.childManager.setDidUnderflow(true); |
|||
return null; |
|||
} |
|||
|
|||
public void collectGarbage(int leadingGarbage, int trailingGarbage) { |
|||
this.invokeLayoutCallback<SliverConstraints>((SliverConstraints constraints) => { |
|||
while (leadingGarbage > 0) { |
|||
this._destroyOrCacheChild(this.firstChild); |
|||
leadingGarbage -= 1; |
|||
} |
|||
|
|||
while (trailingGarbage > 0) { |
|||
this._destroyOrCacheChild(this.lastChild); |
|||
trailingGarbage -= 1; |
|||
} |
|||
|
|||
this._keepAliveBucket.Values.Where((RenderBox child) => { |
|||
var childParentData = (SliverMultiBoxAdaptorParentData) child.parentData; |
|||
return !childParentData.keepAlive; |
|||
}).ToList().ForEach(this._childManager.removeChild); |
|||
}); |
|||
} |
|||
|
|||
public int indexOf(RenderBox child) { |
|||
var childParentData = (SliverMultiBoxAdaptorParentData) child.parentData; |
|||
return childParentData.index; |
|||
} |
|||
|
|||
public double paintExtentOf(RenderBox child) { |
|||
switch (this.constraints.axis) { |
|||
case Axis.horizontal: |
|||
return child.size.width; |
|||
case Axis.vertical: |
|||
return child.size.height; |
|||
} |
|||
|
|||
return 0.0; |
|||
} |
|||
|
|||
public override double childMainAxisPosition(RenderObject child) { |
|||
return this.childScrollOffset(child) - this.constraints.scrollOffset; |
|||
} |
|||
|
|||
public override double childScrollOffset(RenderObject child) { |
|||
var childParentData = (SliverMultiBoxAdaptorParentData) child.parentData; |
|||
return childParentData.layoutOffset; |
|||
} |
|||
|
|||
public override void applyPaintTransform(RenderObject child, ref Matrix4x4 transform) { |
|||
this.applyPaintTransformForBoxChild((RenderBox) child, ref transform); |
|||
} |
|||
|
|||
public override void paint(PaintingContext context, Offset offset) { |
|||
if (this.firstChild == null) { |
|||
return; |
|||
} |
|||
|
|||
Offset mainAxisUnit = null, crossAxisUnit = null, originOffset = null; |
|||
bool addExtent = false; |
|||
switch (GrowthDirectionUtils.applyGrowthDirectionToAxisDirection(this.constraints.axisDirection, |
|||
this.constraints.growthDirection)) { |
|||
case AxisDirection.up: |
|||
mainAxisUnit = new Offset(0.0, -1.0); |
|||
crossAxisUnit = new Offset(1.0, 0.0); |
|||
originOffset = offset + new Offset(0.0, this.geometry.paintExtent); |
|||
addExtent = true; |
|||
break; |
|||
case AxisDirection.right: |
|||
mainAxisUnit = new Offset(1.0, 0.0); |
|||
crossAxisUnit = new Offset(0.0, 1.0); |
|||
originOffset = offset; |
|||
addExtent = false; |
|||
break; |
|||
case AxisDirection.down: |
|||
mainAxisUnit = new Offset(0.0, 1.0); |
|||
crossAxisUnit = new Offset(1.0, 0.0); |
|||
originOffset = offset; |
|||
addExtent = false; |
|||
break; |
|||
case AxisDirection.left: |
|||
mainAxisUnit = new Offset(-1.0, 0.0); |
|||
crossAxisUnit = new Offset(0.0, 1.0); |
|||
originOffset = offset + new Offset(this.geometry.paintExtent, 0.0); |
|||
addExtent = true; |
|||
break; |
|||
} |
|||
|
|||
RenderBox child = this.firstChild; |
|||
while (child != null) { |
|||
double mainAxisDelta = this.childMainAxisPosition(child); |
|||
double crossAxisDelta = this.childCrossAxisPosition(child); |
|||
Offset childOffset = new Offset( |
|||
originOffset.dx + mainAxisUnit.dx * mainAxisDelta + crossAxisUnit.dx * crossAxisDelta, |
|||
originOffset.dy + mainAxisUnit.dy * mainAxisDelta + crossAxisUnit.dy * crossAxisDelta |
|||
); |
|||
if (addExtent) { |
|||
childOffset += mainAxisUnit * this.paintExtentOf(child); |
|||
} |
|||
|
|||
if (mainAxisDelta < this.constraints.remainingPaintExtent && |
|||
mainAxisDelta + this.paintExtentOf(child) > 0) { |
|||
context.paintChild(child, childOffset); |
|||
} |
|||
|
|||
child = this.childAfter(child); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 00853d7738fe40eeb3e7b44db23248be |
|||
timeCreated: 1535502825 |
|
|||
using System; |
|||
using UIWidgets.painting; |
|||
using UIWidgets.ui; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgets.rendering { |
|||
public class RenderSliverPadding : RenderObjectWithChildMixinRenderSliver<RenderSliver> { |
|||
RenderSliverPadding( |
|||
EdgeInsets padding, |
|||
RenderSliver child |
|||
) { |
|||
this._padding = padding; |
|||
this.child = child; |
|||
} |
|||
|
|||
public EdgeInsets padding { |
|||
get { return this._padding; } |
|||
set { |
|||
if (this._padding == value) { |
|||
return; |
|||
} |
|||
|
|||
this._padding = value; |
|||
this.markNeedsLayout(); |
|||
} |
|||
} |
|||
|
|||
public EdgeInsets _padding; |
|||
|
|||
public double beforePadding { |
|||
get { |
|||
switch (GrowthDirectionUtils.applyGrowthDirectionToAxisDirection( |
|||
this.constraints.axisDirection, this.constraints.growthDirection)) { |
|||
case AxisDirection.up: |
|||
return this._padding.bottom; |
|||
case AxisDirection.right: |
|||
return this._padding.left; |
|||
case AxisDirection.down: |
|||
return this._padding.top; |
|||
case AxisDirection.left: |
|||
return this._padding.right; |
|||
} |
|||
|
|||
return 0.0; |
|||
} |
|||
} |
|||
|
|||
public double afterPadding { |
|||
get { |
|||
switch (GrowthDirectionUtils.applyGrowthDirectionToAxisDirection( |
|||
this.constraints.axisDirection, this.constraints.growthDirection)) { |
|||
case AxisDirection.up: |
|||
return this._padding.top; |
|||
case AxisDirection.right: |
|||
return this._padding.right; |
|||
case AxisDirection.down: |
|||
return this._padding.bottom; |
|||
case AxisDirection.left: |
|||
return this._padding.left; |
|||
} |
|||
|
|||
return 0.0; |
|||
} |
|||
} |
|||
|
|||
public double mainAxisPadding { |
|||
get { return this._padding.along(this.constraints.axis); } |
|||
} |
|||
|
|||
public double crossAxisPadding { |
|||
get { |
|||
switch (this.constraints.axis) { |
|||
case Axis.horizontal: |
|||
return this._padding.vertical; |
|||
case Axis.vertical: |
|||
return this._padding.horizontal; |
|||
} |
|||
|
|||
return 0.0; |
|||
} |
|||
} |
|||
|
|||
public override void setupParentData(RenderObject child) { |
|||
if (!(child.parentData is SliverPhysicalParentData)) { |
|||
child.parentData = new SliverPhysicalParentData(); |
|||
} |
|||
} |
|||
|
|||
public override void performLayout() { |
|||
double beforePadding = this.beforePadding; |
|||
double afterPadding = this.afterPadding; |
|||
double mainAxisPadding = this.mainAxisPadding; |
|||
double crossAxisPadding = this.crossAxisPadding; |
|||
if (this.child == null) { |
|||
this.geometry = new SliverGeometry( |
|||
scrollExtent: mainAxisPadding, |
|||
paintExtent: Math.Min(mainAxisPadding, this.constraints.remainingPaintExtent), |
|||
maxPaintExtent: mainAxisPadding |
|||
); |
|||
return; |
|||
} |
|||
|
|||
this.child.layout( |
|||
this.constraints.copyWith( |
|||
scrollOffset: Math.Max(0.0, this.constraints.scrollOffset - beforePadding), |
|||
cacheOrigin: Math.Min(0.0, this.constraints.cacheOrigin + beforePadding), |
|||
overlap: 0.0, |
|||
remainingPaintExtent: this.constraints.remainingPaintExtent - |
|||
this.calculatePaintOffset(this.constraints, from: 0.0, to: beforePadding), |
|||
remainingCacheExtent: this.constraints.remainingCacheExtent - |
|||
this.calculateCacheOffset(this.constraints, from: 0.0, to: beforePadding), |
|||
crossAxisExtent: Math.Max(0.0, this.constraints.crossAxisExtent - crossAxisPadding) |
|||
), |
|||
parentUsesSize: true |
|||
); |
|||
|
|||
SliverGeometry childLayoutGeometry = this.child.geometry; |
|||
if (childLayoutGeometry.scrollOffsetCorrection != 0.0) { |
|||
this.geometry = new SliverGeometry( |
|||
scrollOffsetCorrection: childLayoutGeometry.scrollOffsetCorrection |
|||
); |
|||
return; |
|||
} |
|||
|
|||
double beforePaddingPaintExtent = this.calculatePaintOffset( |
|||
this.constraints, |
|||
from: 0.0, |
|||
to: beforePadding |
|||
); |
|||
|
|||
double afterPaddingPaintExtent = this.calculatePaintOffset( |
|||
this.constraints, |
|||
from: beforePadding + childLayoutGeometry.scrollExtent, |
|||
to: mainAxisPadding + childLayoutGeometry.scrollExtent |
|||
); |
|||
|
|||
double mainAxisPaddingPaintExtent = beforePaddingPaintExtent + afterPaddingPaintExtent; |
|||
double beforePaddingCacheExtent = this.calculateCacheOffset( |
|||
this.constraints, |
|||
from: 0.0, |
|||
to: beforePadding |
|||
); |
|||
double afterPaddingCacheExtent = this.calculateCacheOffset( |
|||
this.constraints, |
|||
from: beforePadding + childLayoutGeometry.scrollExtent, |
|||
to: mainAxisPadding + childLayoutGeometry.scrollExtent |
|||
); |
|||
|
|||
double mainAxisPaddingCacheExtent = afterPaddingCacheExtent + beforePaddingCacheExtent; |
|||
double paintExtent = Math.Min( |
|||
beforePaddingPaintExtent + Math.Max(childLayoutGeometry.paintExtent, |
|||
childLayoutGeometry.layoutExtent + afterPaddingPaintExtent), |
|||
this.constraints.remainingPaintExtent |
|||
); |
|||
|
|||
this.geometry = new SliverGeometry( |
|||
scrollExtent: mainAxisPadding + childLayoutGeometry.scrollExtent, |
|||
paintExtent: paintExtent, |
|||
layoutExtent: Math.Min(mainAxisPaddingPaintExtent + childLayoutGeometry.layoutExtent, paintExtent), |
|||
cacheExtent: Math.Min(mainAxisPaddingCacheExtent + childLayoutGeometry.cacheExtent, |
|||
this.constraints.remainingCacheExtent), |
|||
maxPaintExtent: mainAxisPadding + childLayoutGeometry.maxPaintExtent, |
|||
hitTestExtent: Math.Max( |
|||
mainAxisPaddingPaintExtent + childLayoutGeometry.paintExtent, |
|||
beforePaddingPaintExtent + childLayoutGeometry.hitTestExtent |
|||
), |
|||
hasVisualOverflow: childLayoutGeometry.hasVisualOverflow |
|||
); |
|||
|
|||
var childParentData = (SliverPhysicalParentData) this.child.parentData; |
|||
switch (GrowthDirectionUtils.applyGrowthDirectionToAxisDirection(this.constraints.axisDirection, |
|||
this.constraints.growthDirection)) { |
|||
case AxisDirection.up: |
|||
childParentData.paintOffset = new Offset(this._padding.left, |
|||
this.calculatePaintOffset(this.constraints, |
|||
from: this._padding.bottom + childLayoutGeometry.scrollExtent, |
|||
to: this._padding.bottom + childLayoutGeometry.scrollExtent + this._padding.top)); |
|||
break; |
|||
case AxisDirection.right: |
|||
childParentData.paintOffset = |
|||
new Offset(this.calculatePaintOffset(this.constraints, from: 0.0, to: this._padding.left), |
|||
this._padding.top); |
|||
break; |
|||
case AxisDirection.down: |
|||
childParentData.paintOffset = new Offset(this._padding.left, |
|||
this.calculatePaintOffset(this.constraints, from: 0.0, to: this._padding.top)); |
|||
break; |
|||
case AxisDirection.left: |
|||
childParentData.paintOffset = new Offset( |
|||
this.calculatePaintOffset(this.constraints, |
|||
from: this._padding.right + childLayoutGeometry.scrollExtent, |
|||
to: this._padding.right + childLayoutGeometry.scrollExtent + this._padding.left), |
|||
this._padding.top); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
public override double childMainAxisPosition(RenderObject child) { |
|||
return this.calculatePaintOffset(this.constraints, from: 0.0, to: this.beforePadding); |
|||
} |
|||
|
|||
public override double childCrossAxisPosition(RenderObject child) { |
|||
switch (GrowthDirectionUtils.applyGrowthDirectionToAxisDirection( |
|||
this.constraints.axisDirection, this.constraints.growthDirection)) { |
|||
case AxisDirection.up: |
|||
case AxisDirection.down: |
|||
return this._padding.left; |
|||
case AxisDirection.left: |
|||
case AxisDirection.right: |
|||
return this._padding.top; |
|||
} |
|||
|
|||
return 0.0; |
|||
} |
|||
|
|||
public override double childScrollOffset(RenderObject child) { |
|||
return this.beforePadding; |
|||
} |
|||
|
|||
public override void applyPaintTransform(RenderObject child, ref Matrix4x4 transform) { |
|||
var childParentData = (SliverPhysicalParentData) child.parentData; |
|||
childParentData.applyPaintTransform(ref transform); |
|||
} |
|||
|
|||
public override void paint(PaintingContext context, Offset offset) { |
|||
if (this.child != null && this.child.geometry.visible) { |
|||
var childParentData = (SliverPhysicalParentData) this.child.parentData; |
|||
context.paintChild(this.child, offset + childParentData.paintOffset); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 470336df9a5d4af3a4ccfa5fa759d854 |
|||
timeCreated: 1535523877 |
|
|||
fileFormatVersion: 2 |
|||
guid: 00abcb769ae345788c13376e3170023b |
|||
timeCreated: 1535349060 |
|
|||
namespace UIWidgets.gestures { |
|||
} |
|
|||
namespace UIWidgets.math |
|||
{ |
|||
public class MathUtil |
|||
{ |
|||
public static double Clamp(double value, double min, double max) |
|||
{ |
|||
if ((double) value < (double) min) |
|||
value = min; |
|||
else if ((double) value > (double) max) |
|||
value = max; |
|||
return value; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b170f0bc9c074368b3be9db736974c6f |
|||
timeCreated: 1535421239 |
|
|||
fileFormatVersion: 2 |
|||
guid: 05dff99bf5a0462da5c4c9c7b7d1b3dc |
|||
timeCreated: 1535533476 |
|
|||
fileFormatVersion: 2 |
|||
guid: 8c81399e7e914dedaee84b52748d4ac0 |
|||
timeCreated: 1535349134 |
|
|||
fileFormatVersion: 2 |
|||
guid: 869d4277d11048c397fc1fdb3abe3c02 |
|||
timeCreated: 1535349090 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgets.ui |
|||
{ |
|||
public class StyledRuns |
|||
{ |
|||
private readonly List<TextStyle> styles = new List<TextStyle>(); |
|||
private readonly List<IndexedRun> runs = new List<IndexedRun>(); |
|||
|
|||
public class RunIterator |
|||
{ |
|||
private int _charIndex; |
|||
private int _runIndex; |
|||
private StyledRuns _runs; |
|||
|
|||
public void nextTo(int index) |
|||
{ |
|||
if (_charIndex > index) |
|||
{ |
|||
throw new ArgumentException("can to move back"); |
|||
} |
|||
_charIndex = index; |
|||
while (_runIndex < _runs.size) |
|||
{ |
|||
var run = _runs.getRun(_runIndex); |
|||
if (run.start <= _charIndex && _charIndex < run.end) |
|||
{ |
|||
break; |
|||
} |
|||
_runIndex++; |
|||
} |
|||
} |
|||
|
|||
public Run run |
|||
{ |
|||
get { return _runs.getRun(_runIndex); } |
|||
} |
|||
|
|||
public int charIndex |
|||
{ |
|||
get { return _charIndex; } |
|||
} |
|||
|
|||
public int runIndex |
|||
{ |
|||
get { return _runIndex; } |
|||
} |
|||
|
|||
public bool end |
|||
{ |
|||
get |
|||
{ |
|||
return runIndex >= _runs.size; |
|||
} |
|||
} |
|||
|
|||
internal RunIterator(StyledRuns runs) |
|||
{ |
|||
_charIndex = 0; |
|||
_runIndex = 0; |
|||
_runs = runs; |
|||
} |
|||
|
|||
|
|||
} |
|||
public class Run |
|||
{ |
|||
public readonly TextStyle style; |
|||
public readonly int start; |
|||
public readonly int end; |
|||
|
|||
public Run(TextStyle style, int start, int end) |
|||
{ |
|||
this.style = style; |
|||
this.start = start; |
|||
this.end = end; |
|||
} |
|||
} |
|||
|
|||
public class IndexedRun |
|||
{ |
|||
public readonly int styleIndex = 0; |
|||
public readonly int start; |
|||
public int end; |
|||
|
|||
public IndexedRun(int styleIndex, int start, int end) |
|||
{ |
|||
this.styleIndex = styleIndex; |
|||
this.start = start; |
|||
this.end = end; |
|||
} |
|||
} |
|||
|
|||
public StyledRuns() |
|||
{ |
|||
} |
|||
|
|||
public StyledRuns(StyledRuns other) |
|||
{ |
|||
styles = new List<TextStyle>(other.styles); |
|||
runs = new List<IndexedRun>(other.runs); |
|||
} |
|||
|
|||
public int addStyle(TextStyle style) |
|||
{ |
|||
var styleIndex = styles.Count; |
|||
styles.Add( style); |
|||
return styleIndex; |
|||
|
|||
} |
|||
|
|||
public TextStyle getStyle(int index) |
|||
{ |
|||
return styles[index]; |
|||
} |
|||
|
|||
public void startRun(int styleIndex, int start) |
|||
{ |
|||
endRunIfNeeded(start); |
|||
runs.Add(new IndexedRun(styleIndex, start, start)); |
|||
|
|||
} |
|||
|
|||
public void endRunIfNeeded(int end) |
|||
{ |
|||
var lastIndex = runs.Count - 1; |
|||
if (lastIndex < 0) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var run = runs[lastIndex]; |
|||
if (run.start == end) |
|||
{ |
|||
runs.RemoveAt(lastIndex); |
|||
} |
|||
else |
|||
{ |
|||
run.end = end; |
|||
} |
|||
} |
|||
|
|||
public Run getRun(int index) |
|||
{ |
|||
var run = runs[index]; |
|||
return new Run(styles[run.styleIndex], run.start, run.end); |
|||
} |
|||
|
|||
public RunIterator iterator() |
|||
{ |
|||
return new RunIterator(this); |
|||
} |
|||
|
|||
public int size |
|||
{ |
|||
get { return runs.Count; } |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 3f314adfc2d8498d827e5501599b0f5a |
|||
timeCreated: 1535434217 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgets.ui |
|||
{ |
|||
public class LineBreaker |
|||
{ |
|||
public class LineInfo |
|||
{ |
|||
public int start; |
|||
public double width; |
|||
} |
|||
|
|||
private StyledRuns _runs; |
|||
|
|||
public Vector2[] _characterPositions; |
|||
public float[] _characterWidth; |
|||
private string _text; |
|||
private float _width; |
|||
private int _lineStart; |
|||
private int _wordStart; |
|||
private int _spaceCount = 0; |
|||
private int tabCount = 4; |
|||
private double _lineLength; |
|||
private Font[] _styleRunFonts; |
|||
|
|||
private List<LineInfo> _lines; |
|||
|
|||
public void setup(string text, StyledRuns runs, Font[] styleRunFonts, float width, Vector2[] characterPositions, float[] characterWidth) |
|||
{ |
|||
_text = text; |
|||
_runs = runs; |
|||
_characterPositions = characterPositions; |
|||
_characterWidth = characterWidth; |
|||
_width = width; |
|||
_styleRunFonts = styleRunFonts; |
|||
} |
|||
|
|||
public List<LineInfo> getLines() |
|||
{ |
|||
return _lines; |
|||
} |
|||
|
|||
public void doBreak(int blockStart, int blockEnd) |
|||
{ |
|||
_lines = new List<LineInfo>(); |
|||
_lineStart = blockStart; |
|||
_wordStart = blockStart; |
|||
_spaceCount = 0; |
|||
|
|||
float offsetX = 0.0f; |
|||
var runIterator = _runs.iterator(); |
|||
for (var charIndex = blockStart; charIndex < blockEnd; charIndex++) |
|||
{ |
|||
runIterator.nextTo(charIndex); |
|||
var run = runIterator.run; |
|||
var font = _styleRunFonts[runIterator.runIndex]; |
|||
|
|||
var style = run.style; |
|||
CharacterInfo charInfo; |
|||
|
|||
var result = font.GetCharacterInfo(_text[charIndex], out charInfo, 0, run.style.UnityFontStyle); |
|||
|
|||
if (_text[charIndex] == '\t') |
|||
{ |
|||
_spaceCount++; |
|||
|
|||
font.GetCharacterInfo(' ', out charInfo, |
|||
style.UnityFontSize, style.UnityFontStyle); |
|||
float tabSize = charInfo.advance * tabCount; |
|||
var newX = (float)Math.Floor(((offsetX / tabSize) + 1) * tabSize); |
|||
if (newX > _width && _lineStart != charIndex) |
|||
{ |
|||
_characterWidth[charIndex] = tabSize; |
|||
makeLine(charIndex, charIndex); |
|||
} |
|||
else |
|||
{ |
|||
_characterWidth[charIndex] = newX - offsetX; |
|||
_characterPositions[charIndex].x = offsetX; |
|||
} |
|||
offsetX = _characterPositions[charIndex].x + _characterWidth[charIndex]; |
|||
} |
|||
else if (_text[charIndex] == ' ') |
|||
{ |
|||
_spaceCount++; |
|||
_characterPositions[charIndex].x = offsetX; |
|||
_characterWidth[charIndex] = charInfo.advance; |
|||
offsetX = _characterPositions[charIndex].x + _characterWidth[charIndex]; |
|||
// todo no wrap in space ?
|
|||
} |
|||
else |
|||
{ |
|||
if (_spaceCount > 0 || blockStart == charIndex) |
|||
{ |
|||
_wordStart = charIndex; |
|||
} |
|||
|
|||
_characterPositions[charIndex].x = offsetX; |
|||
_characterWidth[charIndex] = charInfo.advance; |
|||
|
|||
if (offsetX + charInfo.advance > _width && _lineStart != charIndex) |
|||
{ |
|||
if (_lineStart == _wordStart) |
|||
{ |
|||
makeLine(charIndex, charIndex); |
|||
_wordStart = charIndex; |
|||
} |
|||
else |
|||
{ |
|||
makeLine(_wordStart, charIndex); |
|||
} |
|||
} |
|||
|
|||
offsetX = _characterPositions[charIndex].x + _characterWidth[charIndex]; |
|||
_spaceCount = 0; |
|||
} |
|||
|
|||
|
|||
} |
|||
|
|||
makeLine(blockEnd, blockEnd); |
|||
} |
|||
|
|||
|
|||
private void makeLine(int end, int last) |
|||
{ |
|||
Debug.Assert(_lineStart < end); |
|||
Debug.Assert(end <= last); |
|||
_lines.Add(new LineInfo() |
|||
{ |
|||
start = _lineStart, |
|||
width = _characterPositions[end - 1].x + _characterWidth[end - 1], |
|||
}); |
|||
_lineStart = end; |
|||
|
|||
if (end >= _characterPositions.Length) |
|||
{ |
|||
return; |
|||
} |
|||
var offset = new Vector2(-_characterPositions[end].x, 0); |
|||
_characterPositions[end].x = 0; |
|||
if (end < last) |
|||
{ |
|||
Paragraph.offsetCharacters(offset, |
|||
_characterPositions, end + 1, last + 1); |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using JetBrains.Annotations; |
|||
using UnityEngine; |
|||
using UnityEngine.Assertions; |
|||
using UnityEngine.Experimental.UIElements; |
|||
|
|||
namespace UIWidgets.ui |
|||
{ |
|||
public class Paragraph |
|||
{ |
|||
struct Range<T>: IEquatable<Range<T>> |
|||
{ |
|||
public Range(T start, T end) |
|||
{ |
|||
this.start = start; |
|||
this.end = end; |
|||
} |
|||
|
|||
public bool Equals(Range<T> other) |
|||
{ |
|||
return EqualityComparer<T>.Default.Equals(start, other.start) && EqualityComparer<T>.Default.Equals(end, other.end); |
|||
} |
|||
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (ReferenceEquals(null, obj)) return false; |
|||
return obj is Range<T> && Equals((Range<T>) obj); |
|||
} |
|||
|
|||
public override int GetHashCode() |
|||
{ |
|||
unchecked |
|||
{ |
|||
return (EqualityComparer<T>.Default.GetHashCode(start) * 397) ^ EqualityComparer<T>.Default.GetHashCode(end); |
|||
} |
|||
} |
|||
|
|||
public static bool operator ==(Range<T> left, Range<T> right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
public static bool operator !=(Range<T> left, Range<T> right) |
|||
{ |
|||
return !left.Equals(right); |
|||
} |
|||
|
|||
public T start, end; |
|||
|
|||
} |
|||
|
|||
class LineRange |
|||
{ |
|||
public LineRange(int start, int end, int endExcludingWhitespace, int endIncludingNewLine, bool hardBreak) |
|||
{ |
|||
this.start = start; |
|||
this.end = end; |
|||
this.endExcludingWhitespace = endExcludingWhitespace; |
|||
this.endIncludingNewLine = endIncludingNewLine; |
|||
this.hardBreak = hardBreak; |
|||
} |
|||
|
|||
public readonly int start; |
|||
public readonly int end; |
|||
public readonly int endExcludingWhitespace; |
|||
public readonly int endIncludingNewLine; |
|||
public readonly bool hardBreak; |
|||
} |
|||
|
|||
class LayoutContext |
|||
{ |
|||
public int width; |
|||
public int index; |
|||
public Vector2 offset; |
|||
public TextStyle style; |
|||
public Font font; |
|||
public int wordStart; |
|||
public int lineStart; |
|||
public int prevWordEnd; |
|||
} |
|||
|
|||
private static readonly Shader textShader; |
|||
|
|||
static Paragraph() { |
|||
|
|||
textShader = Shader.Find("UIWidgets/Text Shader"); |
|||
if (textShader == null) { |
|||
throw new Exception("UIWidgets/Text Shader Lines not found"); |
|||
} |
|||
} |
|||
|
|||
private bool _needsLayout = true; |
|||
|
|||
private string _text; |
|||
|
|||
private StyledRuns _runs; |
|||
|
|||
private ParagraphStyle _paragraphStyle; |
|||
private List<LineRange> _lineRanges = new List<LineRange>(); |
|||
private List<double> _lineWidths = new List<double>(); |
|||
private Vector2[] _characterPositions; |
|||
private double _maxIntrinsicWidth; |
|||
private double _minIntrinsicWidth; |
|||
private double _alphabeticBaseline; |
|||
private double _ideographicBaseline; |
|||
private Font[] _styleRunFonts; |
|||
private float[] _characterWidths; |
|||
private List<double> _lineHeights = new List<double>(); |
|||
private LayoutContext context; |
|||
private bool _didExceedMaxLines; |
|||
|
|||
// private double _characterWidth;
|
|||
|
|||
private double _width; |
|||
|
|||
public const char CHAR_NBSP = '\u00A0'; |
|||
public static bool isWordSpace(char ch) |
|||
{ |
|||
return ch == ' ' || ch == CHAR_NBSP; |
|||
} |
|||
|
|||
public double height |
|||
{ |
|||
get { return _lineHeights.Count == 0 ? 0 : _lineHeights[_lineHeights.Count - 1]; } |
|||
} |
|||
|
|||
public double minIntrinsicWidth |
|||
{ |
|||
get { return _minIntrinsicWidth; } |
|||
} |
|||
|
|||
public double maxIntrinsicWidth |
|||
{ |
|||
get { return _maxIntrinsicWidth; } |
|||
} |
|||
|
|||
public double width |
|||
{ |
|||
get { return _width; } |
|||
} |
|||
|
|||
|
|||
public double alphabeticBaseline |
|||
{ |
|||
get { return 0.0; } |
|||
} |
|||
|
|||
|
|||
public double ideographicBaseline |
|||
{ |
|||
get { return 0.0; } |
|||
} |
|||
|
|||
public bool didExceedMaxLines |
|||
{ |
|||
get { return _didExceedMaxLines; } |
|||
} |
|||
|
|||
public void paint(Canvas canvas, double x, double y) |
|||
{ |
|||
for (int runIndex = 0; runIndex < _runs.size; ++runIndex) |
|||
{ |
|||
var run = _runs.getRun(runIndex); |
|||
if (run.start < run.end) |
|||
{ |
|||
var font = _styleRunFonts[runIndex]; |
|||
var mesh = generateMesh(x, y, font, run); |
|||
canvas.drawMesh(mesh, font.material); |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
public void layout(ParagraphConstraints constraints) |
|||
{ |
|||
if (!_needsLayout && _width == constraints.width) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
_needsLayout = false; |
|||
_width = Math.Floor(constraints.width); |
|||
|
|||
this.setup(); |
|||
computeLineBreak(); |
|||
|
|||
var maxLines = _paragraphStyle.maxLines ?? 0; |
|||
_didExceedMaxLines = maxLines == 0 || _lineRanges.Count <= maxLines; |
|||
var lineLimits = maxLines == 0 ? _lineRanges.Count : Math.Min(maxLines, _lineRanges.Count); |
|||
layoutLines(lineLimits); |
|||
|
|||
double maxWordWidth = 0; |
|||
for (int lineNumber = 0; lineNumber < lineLimits; ++lineNumber) |
|||
{ |
|||
var line = _lineRanges[lineNumber]; |
|||
var words = findWords(line.start, line.end); |
|||
words.ForEach((word) => |
|||
{ |
|||
Debug.Assert(word.start < word.end); |
|||
double wordWidth = _characterPositions[word.end - 1].x - _characterPositions[word.start].x + |
|||
_characterWidths[word.end - 1]; |
|||
if (wordWidth > maxWordWidth) |
|||
{ |
|||
maxWordWidth = wordWidth; |
|||
} |
|||
}); |
|||
|
|||
} |
|||
|
|||
computeWidthMetrics(maxWordWidth); |
|||
} |
|||
|
|||
|
|||
public void setText(string text, StyledRuns runs) |
|||
{ |
|||
_text = text; |
|||
_runs = runs; |
|||
_needsLayout = true; |
|||
_styleRunFonts = null; |
|||
} |
|||
|
|||
public void setParagraphStyle(ParagraphStyle style) |
|||
{ |
|||
_needsLayout = true; |
|||
_paragraphStyle = style; |
|||
_styleRunFonts = null; |
|||
} |
|||
|
|||
public static void offsetCharacters(Vector2 offset, Vector2[] characterPos, int start, int end) |
|||
{ |
|||
if (characterPos != null) |
|||
{ |
|||
for (int i = start; i < characterPos.Length && i < end; ++i) |
|||
{ |
|||
characterPos[i] = characterPos[i] + offset; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void computeWidthMetrics(double maxWordWidth) |
|||
{ |
|||
_maxIntrinsicWidth = 0; |
|||
double lineBlockWidth = 0; |
|||
for (int i = 0; i < _lineWidths.Count; ++i) |
|||
{ |
|||
var line = _lineRanges[i]; |
|||
lineBlockWidth += _lineWidths[i]; |
|||
if (line.hardBreak) |
|||
{ |
|||
_maxIntrinsicWidth = Math.Max(lineBlockWidth, _maxIntrinsicWidth); |
|||
lineBlockWidth = 0; |
|||
} |
|||
} |
|||
|
|||
if (_paragraphStyle.maxLines == 1 || (((_paragraphStyle.maxLines??0) == 0) && |
|||
!string.IsNullOrEmpty(_paragraphStyle.ellipsis))) |
|||
{ |
|||
_minIntrinsicWidth = _maxIntrinsicWidth; |
|||
} |
|||
else |
|||
{ |
|||
_minIntrinsicWidth = Math.Min(maxWordWidth, _maxIntrinsicWidth); |
|||
} |
|||
} |
|||
|
|||
private void setup() |
|||
{ |
|||
_characterPositions = new Vector2[_text.Length]; |
|||
_lineHeights.Clear(); |
|||
_lineRanges.Clear(); |
|||
_lineWidths.Clear(); |
|||
_characterWidths = new float[_text.Length]; |
|||
if (_styleRunFonts == null) |
|||
{ |
|||
_styleRunFonts = new Font[_runs.size]; |
|||
for (int i = 0; i < _styleRunFonts.Length; ++i) |
|||
{ |
|||
var run = _runs.getRun(i); |
|||
if (run.start < run.end) |
|||
{ |
|||
_styleRunFonts[i] = Font.CreateDynamicFontFromOSFont(run.style.safeFontFamily, |
|||
run.style.UnityFontSize); |
|||
_styleRunFonts[i].material.shader = textShader; |
|||
_styleRunFonts[i].RequestCharactersInTexture(_text.Substring(run.start, run.end - run.start), 0, |
|||
run.style.UnityFontStyle); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void layoutLines(int lineLimits) |
|||
{ |
|||
double yOffset = 0; |
|||
var runIndex = 0; |
|||
double lastDescent = 0.0f; |
|||
for (int lineNumber = 0; lineNumber < lineLimits; lineNumber++) |
|||
{ |
|||
var line = _lineRanges[lineNumber]; |
|||
double maxAscent = 0.0f; |
|||
double maxDescent = 0.0f; |
|||
|
|||
for (;;) |
|||
{ |
|||
var run = _runs.getRun(runIndex); |
|||
if (run.start < run.end && run.start < line.end && run.end > line.start) |
|||
{ |
|||
var font = _styleRunFonts[runIndex]; |
|||
var ascent = font.ascent * (run.style.height??1.0); |
|||
var descent = (font.lineHeight - font.ascent) * (run.style.height??1.0); |
|||
if (ascent > maxAscent) |
|||
{ |
|||
maxAscent = ascent; |
|||
} |
|||
if (descent > maxDescent) |
|||
{ |
|||
maxDescent = descent; |
|||
} |
|||
} |
|||
|
|||
if (runIndex + 1 >= _runs.size) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
if (run.end < line.end) |
|||
{ |
|||
runIndex++; |
|||
} |
|||
else |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (lineNumber == 0) |
|||
{ |
|||
_alphabeticBaseline = maxAscent; |
|||
_ideographicBaseline = maxAscent; // todo Properly implement ideographic_baseline
|
|||
} |
|||
lastDescent = maxDescent; |
|||
yOffset += maxAscent + lastDescent; |
|||
for (var charIndex = line.start; charIndex < line.end; charIndex++) |
|||
{ |
|||
_characterPositions[charIndex].y = (float)yOffset; |
|||
} |
|||
|
|||
_lineHeights.Add((_lineHeights.Count == 0 ? 0 : _lineHeights[_lineHeights.Count - 1]) + |
|||
Math.Round(maxAscent + maxDescent)); |
|||
} |
|||
} |
|||
|
|||
private void computeLineBreak() |
|||
{ |
|||
var newLinePositions = new List<int>(); |
|||
for (var i = 0; i < _text.Length; i++) |
|||
{ |
|||
if (_text[i] == '\n') |
|||
{ |
|||
newLinePositions.Add(i); |
|||
} |
|||
} |
|||
newLinePositions.Add(_text.Length); |
|||
|
|||
|
|||
int runIndex = 0; |
|||
StyledRuns.Run lastRun = null; |
|||
var lineBreaker = new LineBreaker(); |
|||
lineBreaker.setup(_text, _runs, _styleRunFonts, (float)_width, _characterPositions, _characterWidths); |
|||
|
|||
for (var newlineIndex = 0; newlineIndex < newLinePositions.Count; ++newlineIndex) |
|||
{ |
|||
var blockStart = newlineIndex > 0 ? newLinePositions[newlineIndex - 1] + 1 : 0; |
|||
var blockEnd = newLinePositions[newlineIndex]; |
|||
var blockSize = blockEnd - blockStart; |
|||
if (blockSize == 0) |
|||
{ |
|||
_lineRanges.Add(new LineRange(blockStart, blockEnd, blockEnd, blockEnd + 1, true)); |
|||
_lineWidths.Add(0); |
|||
continue; |
|||
} |
|||
|
|||
lineBreaker.doBreak(blockStart, blockEnd); |
|||
var lines = lineBreaker.getLines(); |
|||
for (int i = 0; i < lines.Count; ++i) |
|||
{ |
|||
var line = lines[i]; |
|||
var end = i + 1 < lines.Count ? lines[i + 1].start : blockEnd; |
|||
|
|||
var nonWhiteSpace = end - 1; |
|||
while (nonWhiteSpace >= line.start && _text[nonWhiteSpace] == ' ' || _text[nonWhiteSpace] == '\t') |
|||
{ |
|||
nonWhiteSpace--; |
|||
} |
|||
|
|||
_lineRanges.Add(new LineRange(line.start, end, nonWhiteSpace, end + 1, end == blockEnd)); |
|||
_lineWidths.Add(line.width); |
|||
} |
|||
} |
|||
|
|||
return; |
|||
|
|||
} |
|||
|
|||
private Mesh generateMesh(double x, double y, Font font, StyledRuns.Run run) |
|||
{ |
|||
var vertices = new Vector3[_text.Length * 4]; |
|||
var triangles = new int[_text.Length * 6]; |
|||
var uv = new Vector2[_text.Length * 4]; |
|||
Vector3 offset = new Vector3((float)x, (float)y, 0); |
|||
font.RequestCharactersInTexture(_text.Substring(run.start, run.end - run.start), run.style.UnityFontSize, run.style.UnityFontStyle); |
|||
for (int charIndex = run.start; charIndex < run.end; ++charIndex) |
|||
{ |
|||
CharacterInfo charInfo; |
|||
var result = font.GetCharacterInfo(_text[charIndex], out charInfo, run.style.UnityFontSize, run.style.UnityFontStyle); |
|||
var position = _characterPositions[charIndex]; |
|||
|
|||
vertices[4 * charIndex + 0] = offset + new Vector3(position.x + charInfo.minX, position.y - charInfo.maxY, 0); |
|||
vertices[4 * charIndex + 1] = offset + new Vector3(position.x + charInfo.maxX, position.y - charInfo.maxY, 0); |
|||
vertices[4 * charIndex + 2] = offset + new Vector3(position.x + charInfo.maxX, position.y - charInfo.minY, 0); |
|||
vertices[4 * charIndex + 3] = offset + new Vector3(position.x + charInfo.minX, position.y - charInfo.minY, 0); |
|||
|
|||
if (_text[charIndex] != ' ' && _text[charIndex] != '\t' && _text[charIndex] != '\n') |
|||
{ |
|||
uv[4 * charIndex + 0] = charInfo.uvTopLeft; |
|||
uv[4 * charIndex + 1] = charInfo.uvTopRight; |
|||
uv[4 * charIndex + 2] = charInfo.uvBottomRight; |
|||
uv[4 * charIndex + 3] = charInfo.uvBottomLeft; |
|||
|
|||
} |
|||
else |
|||
{ |
|||
uv[4 * charIndex + 0] = Vector2.zero; |
|||
uv[4 * charIndex + 1] = Vector2.zero; |
|||
uv[4 * charIndex + 2] = Vector2.zero; |
|||
uv[4 * charIndex + 3] = Vector2.zero; |
|||
|
|||
} |
|||
|
|||
triangles[6 * charIndex + 0] = 4 * charIndex + 0; |
|||
triangles[6 * charIndex + 1] = 4 * charIndex + 1; |
|||
triangles[6 * charIndex + 2] = 4 * charIndex + 2; |
|||
|
|||
triangles[6 * charIndex + 3] = 4 * charIndex + 0; |
|||
triangles[6 * charIndex + 4] = 4 * charIndex + 2; |
|||
triangles[6 * charIndex + 5] = 4 * charIndex + 3; |
|||
} |
|||
|
|||
var mesh = new Mesh() |
|||
{ |
|||
vertices = vertices, |
|||
triangles = triangles, |
|||
uv = uv |
|||
}; |
|||
var colors = new UnityEngine.Color[vertices.Length]; |
|||
for (var i = 0; i < colors.Length; i++) |
|||
{ |
|||
colors[i] = run.style.UnityColor; |
|||
} |
|||
|
|||
mesh.colors = colors; |
|||
|
|||
return mesh; |
|||
} |
|||
|
|||
private List<Range<int>> findWords(int start, int end) |
|||
{ |
|||
var inWord = false; |
|||
int wordStart = 0; |
|||
List<Range<int>> words = new List<Range<int>>(); |
|||
for (int i = start; i < end; ++i) { |
|||
bool isSpace = isWordSpace(_text[i]); |
|||
if (!inWord && !isSpace) { |
|||
wordStart = i; |
|||
inWord = true; |
|||
} else if (inWord && isSpace) { |
|||
words.Add(new Range<int>(wordStart, i)); |
|||
inWord = false; |
|||
} |
|||
} |
|||
if (inWord) { |
|||
words.Add(new Range<int>(wordStart, end)); |
|||
} |
|||
|
|||
return words; |
|||
} |
|||
|
|||
} |
|||
} |
|
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace UIWidgets.ui |
|||
{ |
|||
public class ParagraphBuilder |
|||
{ |
|||
private StringBuilder _text = new StringBuilder(); |
|||
private ParagraphStyle _paragraphStyle; |
|||
private StyledRuns _runs = new StyledRuns(); |
|||
private List<int> _styleStack = new List<int>(); |
|||
private int _paragraph_style_index; |
|||
|
|||
public ParagraphBuilder(ParagraphStyle style) |
|||
{ |
|||
setParagraphStyle(style); |
|||
} |
|||
|
|||
public Paragraph build() |
|||
{ |
|||
_runs.endRunIfNeeded(_text.Length); |
|||
var paragraph = new Paragraph(); |
|||
paragraph.setText(_text.ToString(), _runs); |
|||
paragraph.setParagraphStyle(_paragraphStyle); |
|||
return paragraph; |
|||
} |
|||
|
|||
public void pushStyle(TextStyle style) |
|||
{ |
|||
var newStyle = peekStyle().merge(style); |
|||
var styleIndex = _runs.addStyle(newStyle); |
|||
_styleStack.Add(styleIndex); |
|||
_runs.startRun(styleIndex, _text.Length); |
|||
} |
|||
|
|||
public void pop() |
|||
{ |
|||
var lastIndex = _styleStack.Count - 1; |
|||
if (lastIndex < 0) |
|||
{ |
|||
return; |
|||
} |
|||
_styleStack.RemoveAt(lastIndex); |
|||
_runs.startRun(peekStyleIndex(), _text.Length); |
|||
} |
|||
|
|||
public void addText(string text) |
|||
{ |
|||
this._text.Append(text); |
|||
} |
|||
|
|||
public TextStyle peekStyle() |
|||
{ |
|||
return _runs.getStyle(peekStyleIndex()); |
|||
} |
|||
|
|||
|
|||
public int peekStyleIndex() { |
|||
int count = _styleStack.Count; |
|||
if (count > 0) |
|||
{ |
|||
return _styleStack[count - 1]; |
|||
} |
|||
return _paragraph_style_index; |
|||
} |
|||
|
|||
private void setParagraphStyle(ParagraphStyle style) |
|||
{ |
|||
_paragraphStyle = style; |
|||
_paragraph_style_index = _runs.addStyle(style.getTextStyle()); |
|||
_runs.startRun(_paragraph_style_index, _text.Length); |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
撰写
预览
正在加载...
取消
保存
Reference in new issue