kg
6 年前
当前提交
6e8efa4a
共有 51 个文件被更改,包括 5071 次插入 和 13 次删除
-
6.gitignore
-
5Assets/UIWidgets/Tests/Menu.cs
-
11Assets/UIWidgets/painting/basic_types.cs
-
13Assets/UIWidgets/painting/image_provider.cs
-
8Assets/UIWidgets/rendering/object.mixin.gen.cs
-
4Assets/UIWidgets/rendering/object.mixin.njk
-
2Assets/UIWidgets/rendering/shifted_box.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/lib.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
-
3Assets/UIWidgets/ui/txt.meta
-
1001Assets/Plugins/Mono.Data.Sqlite.dll
-
30Assets/Plugins/Mono.Data.Sqlite.dll.meta
-
1001Assets/Plugins/System.Data.dll
-
30Assets/Plugins/System.Data.dll.meta
-
3Assets/UIWidgets/lib/cache_manager.meta
-
3Assets/UIWidgets/lib/cache_manager/cache_manager.cs.meta
-
156Assets/UIWidgets/lib/cache_manager/cache_meta.cs
-
11Assets/UIWidgets/lib/cache_manager/cache_meta.cs.meta
-
346Assets/UIWidgets/lib/cache_manager/cache_manager.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.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: d5b7bf0751a64454ba399aad57ab71fc |
|||
timeCreated: 1535510135 |
|
|||
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 |
|
|||
fileFormatVersion: 2 |
|||
guid: 00abcb769ae345788c13376e3170023b |
|||
timeCreated: 1535349060 |
1001
Assets/Plugins/Mono.Data.Sqlite.dll
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: 3e54fe1804cc94b16a015d1a2d114b55 |
|||
PluginImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
iconMap: {} |
|||
executionOrder: {} |
|||
isPreloaded: 0 |
|||
isOverridable: 0 |
|||
platformData: |
|||
- first: |
|||
Any: |
|||
second: |
|||
enabled: 1 |
|||
settings: {} |
|||
- first: |
|||
Editor: Editor |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
DefaultValueInitialized: true |
|||
- first: |
|||
Windows Store Apps: WindowsStoreApps |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
1001
Assets/Plugins/System.Data.dll
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: acabc02e8ca194aae8d9d3c268ffe747 |
|||
PluginImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
iconMap: {} |
|||
executionOrder: {} |
|||
isPreloaded: 0 |
|||
isOverridable: 0 |
|||
platformData: |
|||
- first: |
|||
Any: |
|||
second: |
|||
enabled: 1 |
|||
settings: {} |
|||
- first: |
|||
Editor: Editor |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
DefaultValueInitialized: true |
|||
- first: |
|||
Windows Store Apps: WindowsStoreApps |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 46a1a2081f1e4842a8ce89f2f0e15234 |
|||
timeCreated: 1535510181 |
|
|||
fileFormatVersion: 2 |
|||
guid: 32cd17d74783499d97a953247420a1b3 |
|||
timeCreated: 1535510219 |
|
|||
using System.Collections.Generic; |
|||
using System; |
|||
using System.IO; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgets.lib.cache_manager { |
|||
public class CacheMeta { |
|||
private static readonly string _directory = Application.persistentDataPath; |
|||
public string relativePath = null; |
|||
public string eTag = null; |
|||
public double touched; |
|||
public double validTill; |
|||
public string url; |
|||
public string key; |
|||
|
|||
public CacheMeta(string url) { |
|||
this.url = url; |
|||
touch(); |
|||
} |
|||
|
|||
private CacheMeta(Builder builder) { |
|||
key = builder.key; |
|||
relativePath = builder.relativePath; |
|||
eTag = builder.eTag; |
|||
touched = builder.touched; |
|||
validTill = builder.validTill; |
|||
url = builder.url; |
|||
} |
|||
|
|||
public string getFilePath() { |
|||
if (this.relativePath == null) { |
|||
return null; |
|||
} |
|||
return _directory + this.relativePath; |
|||
} |
|||
|
|||
public void setRelativePath(string path) { |
|||
this.relativePath = path; |
|||
} |
|||
|
|||
public void touch() { |
|||
this.touched = millisecondsSinceEpoch(DateTime.Now); |
|||
} |
|||
|
|||
public void setDataFromHeaders(Dictionary<string, string> headers) { |
|||
var ageDuration = new TimeSpan(7, 0, 0, 0); // 7 days
|
|||
|
|||
if (headers.ContainsKey("cache-control")) { |
|||
var cacheControl = headers["cache-control"]; |
|||
string[] stringSeparators = {", "}; |
|||
var controlSettings = cacheControl.Split(stringSeparators, StringSplitOptions.None); |
|||
foreach (var controlSetting in controlSettings) { |
|||
if (controlSetting.StartsWith("max-age=")) { |
|||
int validSeconds = 0; |
|||
if (int.TryParse(controlSetting.Split('=')[1], out validSeconds)) { |
|||
if (validSeconds > 0) { |
|||
ageDuration = new TimeSpan(0, 0, validSeconds); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
validTill = millisecondsSinceEpoch(DateTime.Now + ageDuration); |
|||
|
|||
if (headers.ContainsKey("etag")) { |
|||
eTag = headers["etag"]; |
|||
} |
|||
|
|||
var fileExtension = ""; |
|||
if (headers.ContainsKey("content-type")) { |
|||
var type = headers["content-type"].Split('/'); |
|||
if (type.Length == 2) { |
|||
fileExtension = string.Format(".{0}", type[1]); |
|||
} |
|||
} |
|||
|
|||
var oldPath = getFilePath(); |
|||
if (oldPath != null && !oldPath.EndsWith(fileExtension)) { |
|||
removeOldFile(oldPath); |
|||
relativePath = null; |
|||
} |
|||
|
|||
if (relativePath == null) { |
|||
var fileName = string.Format("/cache_{0}{1}", Guid.NewGuid(), fileExtension); |
|||
relativePath = fileName; |
|||
} |
|||
} |
|||
|
|||
private static void removeOldFile(string filePath) { |
|||
if (File.Exists(filePath)) { |
|||
File.Delete(filePath); |
|||
} |
|||
} |
|||
|
|||
public static double millisecondsSinceEpoch(DateTime time) { |
|||
return (time - new DateTime(1970, 1, 1)).TotalMilliseconds; |
|||
} |
|||
|
|||
public static DateTime fromMillisecondsSinceEpoch(double ms) { |
|||
return new DateTime(1970, 1, 1).AddMilliseconds(ms); |
|||
} |
|||
|
|||
public sealed class Builder { |
|||
internal string key { get; private set; } |
|||
|
|||
internal string relativePath { get; private set; } |
|||
|
|||
internal string eTag { get; private set; } |
|||
|
|||
internal double touched { get; private set; } |
|||
|
|||
internal double validTill { get; private set; } |
|||
|
|||
internal string url { get; private set; } |
|||
|
|||
public Builder(string key) { |
|||
if (string.IsNullOrEmpty(key)) { |
|||
throw new ArgumentException("key can't be empty", "key"); |
|||
} |
|||
|
|||
this.key = key; |
|||
} |
|||
|
|||
public Builder RelativePath(string relativePath) { |
|||
this.relativePath = relativePath; |
|||
return this; |
|||
} |
|||
|
|||
public Builder ETag(string eTag) { |
|||
this.eTag = eTag; |
|||
return this; |
|||
} |
|||
|
|||
public Builder Touched(double touched) { |
|||
this.touched = touched; |
|||
return this; |
|||
} |
|||
|
|||
public Builder ValidTill(double validTill) { |
|||
this.validTill = validTill; |
|||
return this; |
|||
} |
|||
|
|||
public Builder Url(string url) { |
|||
this.url = url; |
|||
return this; |
|||
} |
|||
|
|||
public CacheMeta Build() |
|||
{ |
|||
return new CacheMeta(this); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d54df7319b8ca49cf9aba74db3698508 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Security.Cryptography; |
|||
using RSG; |
|||
using System.Text; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using UnityEngine; |
|||
using System.Net; |
|||
using Mono.Data.Sqlite; |
|||
using UIWidgets.painting; |
|||
|
|||
namespace UIWidgets.lib.cache_manager { |
|||
public class CacheManager { |
|||
private readonly string _keyCacheData = "lib_cached_image_data"; |
|||
private readonly string _keyCacheCleanDate = "lib_cached_image_data_last_clean"; |
|||
|
|||
private string _dbUri; |
|||
|
|||
private const string DbFileName = @"ui_widgets_cache.db"; |
|||
|
|||
private static TimeSpan inBetweenCleans = new TimeSpan(7, 0, 0, 0); |
|||
private static TimeSpan maxAgeCacheObject = new TimeSpan(30, 0, 0, 0); |
|||
|
|||
private static int maxNrOfCacheObjects = 2; // configurable ?
|
|||
|
|||
private static CacheManager _instance; |
|||
|
|||
// public DateTime lastCacheClean;
|
|||
private bool _isStoringData = false; |
|||
private bool _shouldStoreDataAgain = false; |
|||
|
|||
public static CacheManager getInstance() { |
|||
if (_instance == null) { |
|||
_instance = new CacheManager(); |
|||
_instance._init(); |
|||
} |
|||
|
|||
return _instance; |
|||
} |
|||
|
|||
private void _init() { |
|||
_setupDatabase(); |
|||
} |
|||
|
|||
private void _setupDatabase() { |
|||
var directoryPath = Application.persistentDataPath; |
|||
var _dbFilePath = Path.Combine(directoryPath, DbFileName); |
|||
_dbUri = "URI=file:" + _dbFilePath; |
|||
|
|||
if (!Directory.Exists(directoryPath)) { |
|||
Directory.CreateDirectory(directoryPath); |
|||
} |
|||
|
|||
if (!File.Exists(_dbFilePath)) { |
|||
SqliteConnection.CreateFile(_dbFilePath); |
|||
} |
|||
|
|||
using (var connection = new SqliteConnection(_dbUri)) { |
|||
connection.Open(); |
|||
const string createCacheTable = @"CREATE TABLE IF NOT EXISTS Cache (
|
|||
Key TEXT NOT NULL PRIMARY KEY, |
|||
FilePath TEXT NOT NULL, |
|||
ETag TEXT, |
|||
Url TEXT NOT NULL, |
|||
Touched REAL, |
|||
ValidTill REAL |
|||
)";
|
|||
|
|||
using (var command = new SqliteCommand(createCacheTable, connection)) { |
|||
command.ExecuteNonQuery(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public IPromise<CacheMeta> getMeta(string url) { |
|||
var key = generateHashKey(url); |
|||
|
|||
CacheMeta meta = null; |
|||
|
|||
using (var connection = new SqliteConnection(_dbUri)) { |
|||
connection.Open(); |
|||
const string metaQuery = @"SELECT Key, FilePath, ETag, Url, Touched, ValidTill FROM Cache
|
|||
WHERE Key = @Key";
|
|||
|
|||
using (var command = new SqliteCommand(metaQuery, connection)) { |
|||
command.Parameters.AddWithValue("@Key", key); |
|||
|
|||
using (var reader = command.ExecuteReader()) { |
|||
if (reader.HasRows && reader.Read()) { |
|||
meta = new CacheMeta.Builder(reader.GetString(0)) |
|||
.RelativePath(reader.GetString(1)) |
|||
.ETag(reader.IsDBNull(2) ? string.Empty : reader.GetString(2)) |
|||
.Url(reader.GetString(3)) |
|||
.Touched(reader.GetDouble(4)) |
|||
.ValidTill(reader.GetDouble(5)) |
|||
.Build(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
var promise = new Promise<CacheMeta>(); |
|||
if (meta == null) { |
|||
meta = new CacheMeta(url); |
|||
} |
|||
|
|||
meta.touch(); |
|||
promise.Resolve(meta); |
|||
return promise; |
|||
} |
|||
|
|||
public IPromise<CacheMeta> downloadFileIfNeeded(CacheMeta meta) { |
|||
var promise = new Promise<CacheMeta>(); // Create promise.
|
|||
var filepath = meta.getFilePath(); |
|||
var fileExpire = meta.validTill == 0.0 || |
|||
CacheMeta.fromMillisecondsSinceEpoch(meta.validTill) < DateTime.Now; |
|||
if (filepath == null || |
|||
fileExpire || |
|||
!File.Exists(filepath)) { |
|||
// download from url
|
|||
WebRequest webRequest = WebRequest.Create(new Uri(meta.url)); |
|||
if (fileExpire && meta.eTag != null) { |
|||
webRequest.Headers.Set("If-None-Match", meta.eTag); |
|||
} |
|||
|
|||
webRequest.BeginGetResponse(result => { |
|||
const int BufferSize = 1024; |
|||
|
|||
var bytes = new byte[BufferSize]; |
|||
var response = webRequest.EndGetResponse(result); |
|||
|
|||
var statusCode = (int) ((HttpWebResponse) response).StatusCode; |
|||
var respHeaders = response.Headers; |
|||
var headerDict = new Dictionary<string, string>(); |
|||
for (int i = 0; i < respHeaders.Count; i++) { |
|||
string header = respHeaders.GetKey(i); |
|||
string value = respHeaders.Get(header); |
|||
headerDict[header] = respHeaders[value]; |
|||
} |
|||
|
|||
if (statusCode == 200) { |
|||
meta.setDataFromHeaders(headerDict); |
|||
|
|||
var stream = response.GetResponseStream(); |
|||
if (stream != null) { |
|||
var localStream = File.Create(meta.getFilePath()); |
|||
int bytesRead; |
|||
while ((bytesRead = stream.Read(bytes, 0, BufferSize)) > 0) { |
|||
localStream.Write(bytes, 0, bytesRead); |
|||
} |
|||
|
|||
stream.Close(); |
|||
localStream.Close(); |
|||
promise.Resolve(meta); |
|||
} |
|||
} else if (statusCode == 304) { |
|||
meta.setDataFromHeaders(headerDict); |
|||
promise.Resolve(meta); |
|||
} |
|||
}, null); |
|||
} |
|||
else { |
|||
promise.Resolve(meta); |
|||
} |
|||
|
|||
return promise; |
|||
} |
|||
|
|||
public IPromise<string> updateMeta(CacheMeta newMeta) { |
|||
var key = generateHashKey(newMeta.url); |
|||
|
|||
const string checkMetaQuery = @"SELECT COUNT(*) FROM Cache WHERE
|
|||
Key = @Key";
|
|||
|
|||
bool recordFound = false; |
|||
|
|||
using (var connection = new SqliteConnection(_dbUri)) { |
|||
connection.Open(); |
|||
using (var command = new SqliteCommand(checkMetaQuery, connection)) { |
|||
command.Parameters.AddWithValue("@Key", key); |
|||
|
|||
using (var reader = command.ExecuteReader()) { |
|||
if (reader.Read()) { |
|||
recordFound = reader.GetInt32(0) > 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (recordFound) { |
|||
const string updateCacheQuery = |
|||
@"UPDATE Cache SET FilePath = @FilePath, ETag = @ETag,
|
|||
Url = @Url, Touched = @Touched, ValidTill = @ValidTill |
|||
WHERE Key = @Key";
|
|||
|
|||
using (var command = new SqliteCommand(updateCacheQuery, connection)) { |
|||
command.Parameters.AddWithValue("@FilePath", newMeta.relativePath); |
|||
command.Parameters.AddWithValue("@ETag", newMeta.eTag); |
|||
command.Parameters.AddWithValue("@Url", newMeta.url); |
|||
command.Parameters.AddWithValue("@Touched", newMeta.touched); |
|||
command.Parameters.AddWithValue("@ValidTill", newMeta.validTill); |
|||
command.Parameters.AddWithValue("@key", key); |
|||
command.ExecuteNonQuery(); |
|||
} |
|||
} |
|||
else { |
|||
const string insertQuery = |
|||
@"INSERT INTO Cache (Key, FilePath, ETag, Url, Touched, ValidTill)
|
|||
VALUES (@Key, @FilePath, @ETag, @Url, @Touched, @ValidTill)";
|
|||
|
|||
using (var command = new SqliteCommand(insertQuery, connection)) { |
|||
command.Parameters.AddWithValue("@Key", key); |
|||
command.Parameters.AddWithValue("@FilePath", newMeta.relativePath); |
|||
command.Parameters.AddWithValue("@ETag", newMeta.eTag); |
|||
command.Parameters.AddWithValue("@Url", newMeta.url); |
|||
command.Parameters.AddWithValue("@Touched", newMeta.touched); |
|||
command.Parameters.AddWithValue("@ValidTill", newMeta.validTill); |
|||
command.ExecuteNonQuery(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
_removeOldObjectsFromCache(); |
|||
_shrinkLargeCache(); |
|||
|
|||
var promise = new Promise<string>(); |
|||
promise.Resolve(newMeta.getFilePath()); |
|||
return promise; |
|||
} |
|||
|
|||
public IPromise<ImageInfo> loadCacheFile(string path) { |
|||
var promise = new Promise<ImageInfo>(); |
|||
var bytes = File.ReadAllBytes(path); |
|||
var imageInfo = new ImageInfo(new ui.Image( |
|||
bytes |
|||
)); |
|||
promise.Resolve(imageInfo); |
|||
return promise; |
|||
} |
|||
|
|||
private void _removeOldObjectsFromCache() { |
|||
var oldestDataAllowed = DateTime.Now - maxAgeCacheObject; |
|||
var metas = new List<CacheMeta>(); |
|||
|
|||
const string query = @"SELECT Key, FilePath, ETag, Url, Touched, ValidTill
|
|||
FROM Cache |
|||
WHERE Touched < @OldestTouchedAllowed";
|
|||
|
|||
const string deleteQuery = @"DELETE FROM Cache WHERE Key in (@KeyList)"; |
|||
using (var connection = new SqliteConnection(_dbUri)) { |
|||
connection.Open(); |
|||
using (var command = new SqliteCommand(query, connection)) { |
|||
command.Parameters.AddWithValue("@OldestTouchedAllowed", |
|||
CacheMeta.millisecondsSinceEpoch(oldestDataAllowed)); |
|||
using (var reader = command.ExecuteReader()) { |
|||
while (reader.HasRows && reader.Read()) { |
|||
metas.Add( |
|||
new CacheMeta.Builder(reader.GetString(0)) |
|||
.RelativePath(reader.GetString(1)) |
|||
.ETag(reader.IsDBNull(2) ? string.Empty : reader.GetString(2)) |
|||
.Url(reader.GetString(3)) |
|||
.Touched(reader.GetDouble(4)) |
|||
.ValidTill(reader.GetDouble(5)) |
|||
.Build() |
|||
); |
|||
} |
|||
} |
|||
} |
|||
|
|||
foreach (var meta in metas) { |
|||
File.Delete(meta.getFilePath()); |
|||
} |
|||
|
|||
using (var command = new SqliteCommand(deleteQuery, connection)) { |
|||
command.Parameters.AddWithValue("@KeyList", |
|||
string.Join(",", metas.Select(m => m.key.ToString()).ToArray())); |
|||
command.ExecuteNonQuery(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void _shrinkLargeCache() { |
|||
const string countQuery = @"SELECT COUNT(*) FROM Cache"; |
|||
var totalRecord = 0; |
|||
using (var connection = new SqliteConnection(_dbUri)) { |
|||
connection.Open(); |
|||
using (var command = new SqliteCommand(countQuery, connection)) { |
|||
using (var reader = command.ExecuteReader()) { |
|||
if (reader.Read()) { |
|||
totalRecord = reader.GetInt32(0); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (totalRecord > maxNrOfCacheObjects) { |
|||
var metas = new List<CacheMeta>(); |
|||
var overflow = totalRecord - maxNrOfCacheObjects; |
|||
const string overflowQuery = @"SELECT Key, FilePath, ETag, Url, Touched, ValidTill
|
|||
FROM Cache ORDER BY Touched LIMIT @Overflow";
|
|||
using (var command = new SqliteCommand(overflowQuery, connection)) { |
|||
command.Parameters.AddWithValue("@Overflow", overflow); |
|||
using (var reader = command.ExecuteReader()) { |
|||
while (reader.HasRows && reader.Read()) { |
|||
metas.Add( |
|||
new CacheMeta.Builder(reader.GetString(0)) |
|||
.RelativePath(reader.GetString(1)) |
|||
.ETag(reader.IsDBNull(2) ? string.Empty : reader.GetString(2)) |
|||
.Url(reader.GetString(3)) |
|||
.Touched(reader.GetDouble(4)) |
|||
.ValidTill(reader.GetDouble(5)) |
|||
.Build() |
|||
); |
|||
} |
|||
} |
|||
} |
|||
|
|||
foreach (var meta in metas) { |
|||
File.Delete(meta.getFilePath()); |
|||
} |
|||
|
|||
const string deleteQuery = @"DELETE FROM Cache WHERE Key in (@KeyList)"; |
|||
using (var command = new SqliteCommand(deleteQuery, connection)) { |
|||
command.Parameters.AddWithValue("@KeyList", |
|||
string.Join(",", metas.Select(m => m.key.ToString()).ToArray())); |
|||
command.ExecuteNonQuery(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static string generateHashKey(string url) { |
|||
using (var md5 = MD5.Create()) { |
|||
byte[] inputBytes = Encoding.ASCII.GetBytes(url); |
|||
byte[] hashBytes = md5.ComputeHash(inputBytes); |
|||
|
|||
// Convert the byte array to hexadecimal string
|
|||
StringBuilder sb = new StringBuilder(); |
|||
for (int i = 0; i < hashBytes.Length; i++) { |
|||
sb.Append(hashBytes[i].ToString("X2")); |
|||
} |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
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