浏览代码

Merge pull request #65 from fzhangtj/text_edit

fix font size 0 issue & text background
/main
GitHub 6 年前
当前提交
e05771c4
共有 17 个文件被更改,包括 245 次插入117 次删除
  1. 37
      Runtime/painting/text_style.cs
  2. 2
      Runtime/ui/painting/canvas_impl.cs
  3. 63
      Runtime/ui/painting/txt/font_manager.cs
  4. 29
      Runtime/ui/painting/txt/mesh_generator.cs
  5. 23
      Runtime/ui/painting/txt/text_blob.cs
  6. 39
      Runtime/ui/text.cs
  7. 12
      Runtime/ui/txt/layout.cs
  8. 17
      Runtime/ui/txt/linebreaker.cs
  9. 2
      Runtime/ui/txt/paint_record.cs
  10. 51
      Runtime/ui/txt/paragraph.cs
  11. 12
      Runtime/ui/txt/paragraph_builder.cs
  12. 6
      Runtime/ui/txt/styled_runs.cs
  13. 2
      Runtime/ui/txt/word_separate.cs
  14. 2
      Runtime/ui/txt/wordbreaker.cs
  15. 11
      Tests/Editor/CanvasAndLayers.cs
  16. 43
      Samples/UIWidgetSample/txt/TextStyleSample.cs
  17. 11
      Samples/UIWidgetSample/txt/TextStyleSample.cs.meta

37
Runtime/painting/text_style.cs


using Unity.UIWidgets.ui;
namespace Unity.UIWidgets.painting {
public class TextStyle : Diagnosticable, IEquatable<TextStyle>, ParagraphBuilder.ITextStyleProvider {
public class TextStyle : Diagnosticable, IEquatable<TextStyle> {
public static readonly float _defaultFontSize = 14.0f;
public readonly bool inherit;
public readonly Color color;

this.fontFamily = fontFamily;
this.debugLabel = debugLabel;
this.background = background;
}
public ui.TextStyle getTextStyle(ui.TextStyle currentStyle = null, float textScaleFactor = 1.0f) {
if (currentStyle != null) {
return new ui.TextStyle(
color: this.color ?? currentStyle.color,
fontSize: this.fontSize != null ? this.fontSize * textScaleFactor : currentStyle.fontSize,
fontWeight: this.fontWeight ?? currentStyle.fontWeight,
fontStyle: this.fontStyle ?? currentStyle.fontStyle,
letterSpacing: this.letterSpacing ?? currentStyle.letterSpacing,
wordSpacing: this.wordSpacing ?? currentStyle.wordSpacing,
textBaseline: this.textBaseline ?? currentStyle.textBaseline,
height: this.height ?? currentStyle.height,
decoration: this.decoration ?? currentStyle.decoration,
decorationColor: this.decorationColor ?? currentStyle.decorationColor,
fontFamily: this.fontFamily ?? currentStyle.fontFamily,
background: this.background ?? currentStyle.background
);
}
return new ui.TextStyle(
color: this.color,
fontSize: this.fontSize * textScaleFactor,
fontWeight: this.fontWeight,
fontStyle: this.fontStyle,
letterSpacing: this.letterSpacing,
wordSpacing: this.wordSpacing,
textBaseline: this.textBaseline,
height: this.height,
decoration: this.decoration,
decorationColor: this.decorationColor,
fontFamily: this.fontFamily,
background: this.background
);
}
public RenderComparison compareTo(TextStyle other) {

decoration: other.decoration,
decorationColor: other.decorationColor,
decorationStyle: other.decorationStyle,
background: other.background,
debugLabel: mergedDebugLabel
);
}

2
Runtime/ui/painting/canvas_impl.cs


var font = FontManager.instance.getOrCreate(textBlob.style.fontFamily, style.fontWeight, style.fontStyle).font;
var fontSizeToLoad = Mathf.CeilToInt(style.UnityFontSize * scale);
var subText = textBlob.text.Substring(textBlob.textOffset, textBlob.textSize);
font.RequestCharactersInTexture(subText, fontSizeToLoad, style.UnityFontStyle);
font.RequestCharactersInTextureSafe(subText, fontSizeToLoad, style.UnityFontStyle);
var tex = font.material.mainTexture;

63
Runtime/ui/painting/txt/font_manager.cs


}
class GlyphInfo {
public static GlyphInfo empty = new GlyphInfo(Rect.zero,0, 0, new Vector2(0, 0),
new Vector2(0, 0), new Vector2(0, 0), new Vector2(0, 0));
public readonly Rect rect;
public readonly float advance;
public readonly float glyphHeight;
public readonly Vector2 uvTopLeft;
public readonly Vector2 uvTopRight;
public readonly Vector2 uvBottomLeft;
public readonly Vector2 uvBottomRight;
public GlyphInfo(CharacterInfo info) {
this.rect = Rect.fromLTRB(info.minX, -info.maxY, info.maxX, -info.minY);
this.advance = info.advance;
this.glyphHeight = info.glyphHeight;
this.uvTopLeft = info.uvTopLeft;
this.uvTopRight = info.uvTopRight;
this.uvBottomLeft = info.uvBottomLeft;
this.uvBottomRight = info.uvBottomRight;
}
public GlyphInfo(Rect rect, float advance, float glyphHeight,
Vector2 uvTopLeft, Vector2 uvTopRight, Vector2 uvBottomLeft, Vector2 uvBottomRight) {
this.rect = rect;
this.advance = advance;
this.glyphHeight = glyphHeight;
this.uvTopLeft = uvTopLeft;
this.uvTopRight = uvTopRight;
this.uvBottomLeft = uvBottomLeft;
this.uvBottomRight = uvBottomRight;
}
}
{
public static CharacterInfo getCharacterInfo(this Font font, char ch, int fontSize, UnityEngine.FontStyle fontStyle)
{
{
internal static GlyphInfo getGlyphInfo(this Font font, char ch, int fontSize, UnityEngine.FontStyle fontStyle) {
if (fontSize <= 0) {
return GlyphInfo.empty;
}
if (!success) {
Debug.LogWarning($"character info not found from the given font: character '{ch}' (code{(int)ch}) font: ${font.name}");
if (!success) {
if (!char.IsControl(ch)) {
Debug.LogWarning($"character info not found from the given font: character '{ch}' (code{(int)ch}) font: ${font.name}");
}
return GlyphInfo.empty;
}
return new GlyphInfo(info);
}
internal static void RequestCharactersInTextureSafe(this Font font, string text, int fontSize,
UnityEngine.FontStyle fontStyle = UnityEngine.FontStyle.Normal) {
if (fontSize <= 0) {
return;
return info;
font.RequestCharactersInTexture(text, fontSize, fontStyle);
}
}
}

29
Runtime/ui/painting/txt/mesh_generator.cs


continue;
}
CharacterInfo charInfo = font.getCharacterInfo(ch, fontSizeToLoad, style.UnityFontStyle);
var minX = charInfo.minX / this._scale;
var maxX = charInfo.maxX / this._scale;
var minY = charInfo.minY / this._scale;
var maxY = charInfo.maxY / this._scale;
if (fontSizeToLoad == 0) {
continue;
}
var glyphInfo = font.getGlyphInfo(ch, fontSizeToLoad, style.UnityFontStyle);
var minX = glyphInfo.rect.left / this._scale;
var maxX = glyphInfo.rect.right / this._scale;
var minY = glyphInfo.rect.top / this._scale;
var maxY = glyphInfo.rect.bottom / this._scale;
vertices.Add(new Vector3((position.x + minX), (position.y - maxY), 0));
vertices.Add(new Vector3((position.x + maxX), (position.y - maxY), 0));
vertices.Add(new Vector3((position.x + maxX), (position.y - minY), 0));
vertices.Add(new Vector3((position.x + minX), (position.y - minY), 0));
vertices.Add(new Vector3((position.x + minX), (position.y + minY), 0));
vertices.Add(new Vector3((position.x + maxX), (position.y + minY), 0));
vertices.Add(new Vector3((position.x + maxX), (position.y + maxY), 0));
vertices.Add(new Vector3((position.x + minX), (position.y + maxY), 0));
triangles.Add(baseIndex);
triangles.Add(baseIndex + 1);

triangles.Add(baseIndex + 3);
uv.Add(charInfo.uvTopLeft);
uv.Add(charInfo.uvTopRight);
uv.Add(charInfo.uvBottomRight);
uv.Add(charInfo.uvBottomLeft);
uv.Add(glyphInfo.uvTopLeft);
uv.Add(glyphInfo.uvTopRight);
uv.Add(glyphInfo.uvBottomRight);
uv.Add(glyphInfo.uvBottomLeft);
}
if (vertices.Count == 0) {

23
Runtime/ui/painting/txt/text_blob.cs


namespace Unity.UIWidgets.ui {
public class TextBlob {
public TextBlob(string text, int textOffset, int textSize, Vector2d[] positions, Rect bounds, TextStyle style) {
internal TextBlob(string text, int textOffset, int textSize, Vector2d[] positions, Rect bounds, TextStyle style) {
this.instanceId = ++_nextInstanceId;
this.positions = positions;
this.text = text;

}
static long _nextInstanceId = 0;
public readonly long instanceId;
public readonly string text;
public readonly int textOffset;
public readonly int textSize;
public readonly Vector2d[] positions;
public readonly TextStyle style;
public readonly Rect bounds; // bounds with positions[start] as origin
internal readonly long instanceId;
internal readonly string text;
internal readonly int textOffset;
internal readonly int textSize;
internal readonly Vector2d[] positions;
internal readonly TextStyle style;
internal readonly Rect bounds; // bounds with positions[start] as origin
}
public class TextBlobBuilder {

int _size;
Rect _bounds;
public void allocRunPos(TextStyle style, string text, int offset, int size) {
public void allocRunPos(painting.TextStyle style, string text, int offset, int size, float textScaleFactor = 1.0f) {
this.allocRunPos(TextStyle.applyStyle(null, style, textScaleFactor), text, offset, size);
}
internal void allocRunPos(TextStyle style, string text, int offset, int size) {
this._style = style;
this._text = text;
this._textOffset = offset;

39
Runtime/ui/text.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.foundation;
namespace Unity.UIWidgets.ui {
public enum FontStyle {

}
}
public class TextStyle : IEquatable<TextStyle> {
internal class TextStyle : IEquatable<TextStyle> {
public readonly Color color = Color.fromARGB(255, 0, 0, 0);
public readonly float fontSize = 14.0f;
public readonly FontWeight fontWeight = FontWeight.w400;

internal int UnityFontSize {
get { return (int) this.fontSize; }
}
public static TextStyle applyStyle(TextStyle currentStyle, painting.TextStyle style, float textScaleFactor) {
if (currentStyle != null) {
return new ui.TextStyle(
color: style.color ?? currentStyle.color,
fontSize: style.fontSize != null ? style.fontSize * textScaleFactor : currentStyle.fontSize,
fontWeight: style.fontWeight ?? currentStyle.fontWeight,
fontStyle: style.fontStyle ?? currentStyle.fontStyle,
letterSpacing: style.letterSpacing ?? currentStyle.letterSpacing,
wordSpacing: style.wordSpacing ?? currentStyle.wordSpacing,
textBaseline: style.textBaseline ?? currentStyle.textBaseline,
height: style.height ?? currentStyle.height,
decoration: style.decoration ?? currentStyle.decoration,
decorationColor: style.decorationColor ?? currentStyle.decorationColor,
fontFamily: style.fontFamily ?? currentStyle.fontFamily,
background: style.background ?? currentStyle.background
);
}
return new ui.TextStyle(
color: style.color,
fontSize: style.fontSize * textScaleFactor,
fontWeight: style.fontWeight,
fontStyle: style.fontStyle,
letterSpacing: style.letterSpacing,
wordSpacing: style.wordSpacing,
textBaseline: style.textBaseline,
height: style.height,
decoration: style.decoration,
decorationColor: style.decorationColor,
fontFamily: style.fontFamily,
background: style.background
);
}
public bool Equals(TextStyle other) {

}
}
public TextStyle getTextStyle() {
internal TextStyle getTextStyle() {
return new TextStyle(
fontWeight: this.fontWeight,
fontStyle: this.fontStyle,

12
Runtime/ui/txt/layout.cs


using UnityEngine;
namespace Unity.UIWidgets.ui {
public class Layout {
class Layout {
int _start;
int _count;
List<float> _advances = new List<float>();

this._positions.Clear();
this._count = count;
var font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font;
font.RequestCharactersInTexture(this._text.Substring(start, count),
font.RequestCharactersInTextureSafe(this._text.Substring(start, count),
style.UnityFontSize,
style.UnityFontStyle);

int charIndex = start + i;
var ch = text[charIndex];
CharacterInfo characterInfo = font.getCharacterInfo(ch, style.UnityFontSize, style.UnityFontStyle);
var glyphInfo = font.getGlyphInfo(ch, style.UnityFontSize, style.UnityFontStyle);
var rect = Rect.fromLTRB(characterInfo.minX, -characterInfo.maxY, characterInfo.maxX,
-characterInfo.minY);
var rect = glyphInfo.rect;
rect = rect.translate(this._advance, 0);
if (this._bounds == null || this._bounds.isEmpty) {
this._bounds = rect;

}
this._positions.Add(this._advance);
float advance = characterInfo.advance;
float advance = glyphInfo.advance;
if (ch == '\t') {
advance = this._tabStops.nextTab((this._advance + offset)) - this._advance;
}

17
Runtime/ui/txt/linebreaker.cs


using System.Collections.Generic;
using System;
using System.Collections.Generic;
public class TabStops {
class TabStops {
int _tabWidth = int.MaxValue;
Font _font;

}
if (this._tabWidth == int.MaxValue) {
this._font.RequestCharactersInTexture(" ", this._fontSize);
CharacterInfo characterInfo = this._font.getCharacterInfo(' ', this._fontSize, UnityEngine.FontStyle.Normal);
this._tabWidth = characterInfo.advance * kTabSpaceCount;
this._font.RequestCharactersInTextureSafe(" ", this._fontSize);
if (this._fontSize > 0) {
var glyphInfo = this._font.getGlyphInfo(' ', this._fontSize, UnityEngine.FontStyle.Normal);
this._tabWidth = (int)Math.Round(glyphInfo.advance * kTabSpaceCount);
}
}
if (this._tabWidth == 0) {

}
}
public class Candidate {
class Candidate {
public int offset;
public int pre;
public float preBreak;

public int postSpaceCount;
}
public class LineBreaker {
class LineBreaker {
const float ScoreInfty = float.MaxValue;
const float ScoreDesperate = 1e10f;

2
Runtime/ui/txt/paint_record.cs


namespace Unity.UIWidgets.ui {
public class PaintRecord {
class PaintRecord {
public PaintRecord(TextStyle style, Offset offset, TextBlob _text,
FontMetrics metrics,
int line, float runWidth) {

51
Runtime/ui/txt/paragraph.cs


}
}
public class CodeUnitRun {
class CodeUnitRun {
public readonly int lineNumber;
public readonly TextDirection direction;
public readonly Range<int> codeUnits;

}
public class FontMetrics {
class FontMetrics {
public readonly float ascent;
public readonly float leading = 0.0f;
public readonly float descent;

public static FontMetrics fromFont(Font font, int fontSize) {
var ascent = -font.ascent * fontSize / font.fontSize;
var descent = (font.lineHeight - font.ascent) * fontSize / font.fontSize;
float? fxHeight = null;
font.RequestCharactersInTexture("x", fontSize);
CharacterInfo charInfo;
if (font.GetCharacterInfo('x', out charInfo, fontSize)) {
fxHeight = charInfo.glyphHeight;
}
font.RequestCharactersInTextureSafe("x", fontSize, UnityEngine.FontStyle.Normal);
var glyphInfo = font.getGlyphInfo('x', fontSize, UnityEngine.FontStyle.Normal);
float fxHeight = glyphInfo.glyphHeight;
public class LineStyleRun {
class LineStyleRun {
public readonly int start;
public readonly int end;
public readonly TextStyle style;

}
}
public class PositionWithAffinity {
class PositionWithAffinity {
public readonly int position;
public readonly TextAffinity affinity;

}
}
public class GlyphPosition {
class GlyphPosition {
public readonly Range<float> xPos;
public readonly Range<int> codeUnits;

}
}
public class Range<T> : IEquatable<Range<T>> {
class Range<T> : IEquatable<Range<T>> {
public Range(T start, T end) {
this.start = start;
this.end = end;

public readonly T start, end;
}
public static class RangeUtils {
static class RangeUtils {
public static Range<float> shift(Range<float> value, float shift) {
return new Range<float>(value.start + shift, value.end + shift);
}

public class GlyphLine {
class GlyphLine {
public readonly List<GlyphPosition> positions;
public readonly int totalCountUnits;

}
public void paint(Canvas canvas, Offset offset) {
foreach (var paintRecord in this._paintRecords) {
this.paintBackground(canvas, paintRecord, offset);
}
foreach (var paintRecord in this._paintRecords) {
var paint = new Paint {
filterMode = FilterMode.Bilinear,

}
public void setText(string text, StyledRuns runs) {
internal void setText(string text, StyledRuns runs) {
this._text = text;
this._runs = runs;
this._needsLayout = true;

return TextBox.fromLTBD(0, top, 0, bottom, TextDirection.ltr);
}
public PositionWithAffinity getGlyphPositionAtCoordinate(float dx, float dy) {
internal PositionWithAffinity getGlyphPositionAtCoordinate(float dx, float dy) {
if (this._lineHeights.Count == 0) {
return new PositionWithAffinity(0, TextAffinity.downstream);
}

return Mathf.Max(lineCount - 1, 0);
}
public LineRange getLineRange(int lineIndex) {
internal LineRange getLineRange(int lineIndex) {
public Range<int> getWordBoundary(int offset) {
internal Range<int> getWordBoundary(int offset) {
WordSeparate s = new WordSeparate(this._text);
return s.findWordRange(offset);
}

}
}
void paintBackground(Canvas canvas, PaintRecord record, Offset baseOffset) {
if (record.style.background == null) {
return;
}
var metrics = record.metrics;
Rect rect = Rect.fromLTRB(0, metrics.ascent, record.runWidth, metrics.descent);
rect = rect.shift(baseOffset + record.offset);
canvas.drawRect(rect, record.style.background);
}
float getLineXOffset(float lineTotalAdvance) {
if (this._width.isInfinite()) {
return 0;

}
}
public class SplayTree<TKey, TValue> : IDictionary<TKey, TValue> where TKey : IComparable<TKey> {
class SplayTree<TKey, TValue> : IDictionary<TKey, TValue> where TKey : IComparable<TKey> {
SplayTreeNode root;
int count;
int version = 0;

12
Runtime/ui/txt/paragraph_builder.cs


List<int> _styleStack = new List<int>();
int _paragraph_style_index;
public interface ITextStyleProvider {
TextStyle getTextStyle(TextStyle current = null, float textScaleFactor = 1.0f);
}
public ParagraphBuilder(ParagraphStyle style) {
this.setParagraphStyle(style);
}

return paragraph;
}
public void pushStyle(ITextStyleProvider style, float textScaleFactor) {
var newStyle = style.getTextStyle(this.peekStyle(), textScaleFactor: textScaleFactor);
public void pushStyle(painting.TextStyle style, float textScaleFactor) {
var newStyle = TextStyle.applyStyle(this.peekStyle(), style, textScaleFactor: textScaleFactor);
public void pushStyle(TextStyle style) {
internal void pushStyle(TextStyle style) {
var styleIndex = this._runs.addStyle(style);
this._styleStack.Add(styleIndex);
this._runs.startRun(styleIndex, this._text.Length);

this._text.Append(text);
}
public TextStyle peekStyle() {
internal TextStyle peekStyle() {
return this._runs.getStyle(this.peekStyleIndex());
}

6
Runtime/ui/txt/styled_runs.cs


using System.Collections.Generic;
namespace Unity.UIWidgets.ui {
public class StyledRuns {
class StyledRuns {
readonly List<TextStyle> styles = new List<TextStyle>();
readonly List<IndexedRun> runs = new List<IndexedRun>();

}
}
public class Run {
internal class Run {
public readonly TextStyle style;
public readonly int start;
public readonly int end;

}
}
public class IndexedRun {
internal class IndexedRun {
public readonly int styleIndex = 0;
public readonly int start;
public int end;

2
Runtime/ui/txt/word_separate.cs


namespace Unity.UIWidgets.ui {
public class WordSeparate {
class WordSeparate {
enum Direction {
Forward,
Backward,

2
Runtime/ui/txt/wordbreaker.cs


namespace Unity.UIWidgets.ui {
public class WordBreaker {
class WordBreaker {
public const uint U16_SURROGATE_OFFSET = ((0xd800 << 10) + 0xdc00 - 0x10000);
string _text;
int _offset;

11
Tests/Editor/CanvasAndLayers.cs


using Gradient = Unity.UIWidgets.ui.Gradient;
using Material = UnityEngine.Material;
using Rect = UnityEngine.Rect;
using TextStyle = Unity.UIWidgets.ui.TextStyle;
namespace UIWidgets.Tests {
public class CanvasAndLayers : EditorWindow {

paint);
canvas.scale(3);
TextBlob textBlob = new TextBlob("This is a text blob", 0, 19, new Vector2d[] {
TextBlobBuilder builder = new TextBlobBuilder();
string text = "This is a text blob";
builder.allocRunPos(new TextStyle(), text, 0, text.Length);
builder.setBounds(Unity.UIWidgets.ui.Rect.fromLTWH(0, 0, 200, 50));
builder.positions = new Vector2d[] {
new Vector2d(10, 0),
new Vector2d(20, 0),
new Vector2d(30, 0),

new Vector2d(170, 0),
new Vector2d(180, 0),
new Vector2d(190, 0),
}, Unity.UIWidgets.ui.Rect.fromLTWH(0, 0, 200, 50), new TextStyle());
};
canvas.drawTextBlob(textBlob, new Offset(100, 100), paint);
canvas.drawTextBlob(builder.make(), new Offset(100, 100), paint);
canvas.drawLine(
new Offset(10, 30),

43
Samples/UIWidgetSample/txt/TextStyleSample.cs


using System.Collections.Generic;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
namespace UIWidgetsSample {
public class TextStyleSample : UIWidgetsSamplePanel {
protected override Widget createWidget() {
return new MaterialApp(
title: "Text Style",
home: new TextStyleSampleWidget()
);
}
}
class TextStyleSampleWidget : StatelessWidget {
public override Widget build(BuildContext context) {
var fontStyleTexts = new List<Widget> {
new Text("text", style: new TextStyle(fontSize: 18)),
new Text("text with font size 0 below", style: new TextStyle(fontSize: 14)),
new Text("font size 0", style: new TextStyle(fontSize: 0)),
new Text("text with font size 0 above", style: new TextStyle(fontSize: 14)),
new Text("text with font size 0.3f", style: new TextStyle(fontSize: 0.3f)),
new Text("Text with background", style: new TextStyle(fontSize: 14, background:
new Paint(){color = new Color(0xFF00FF00)})),
};
return new Scaffold(
appBar: new AppBar(
title: new Text("Text Style")
),
body: new Card(
child: new DefaultTextStyle(
style: new TextStyle(fontSize: 40, fontFamily: "Roboto"),
child: new ListView(children: fontStyleTexts))
)
);
}
}
}

11
Samples/UIWidgetSample/txt/TextStyleSample.cs.meta


fileFormatVersion: 2
guid: 1de691b215926469a959e54492262ff4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存