浏览代码

Merge pull request #132 from fzhangtj/master

letter&word spacing
/main
GitHub 6 年前
当前提交
d18becd3
共有 9 个文件被更改,包括 205 次插入65 次删除
  1. 17
      Runtime/foundation/basic_types.cs
  2. 80
      Runtime/ui/txt/layout.cs
  3. 57
      Runtime/ui/txt/layout_utils.cs
  4. 16
      Runtime/ui/txt/linebreaker.cs
  5. 10
      Runtime/ui/txt/paragraph.cs
  6. 8
      Runtime/ui/txt/word_separate.cs
  7. 48
      Runtime/ui/txt/wordbreaker.cs
  8. 4
      Samples/UIWidgetSample/txt/TextStyleSample.cs
  9. 30
      Runtime/ui/txt/text_buff.cs

17
Runtime/foundation/basic_types.cs


}
return "{ " + string.Join(", ", it.Select(item => item.ToString())) + " }";
}
public static void resize<T>(this List<T> list, int size, T value) {
int curSize = list.Count;
if (size < curSize) {
list.RemoveRange(size, curSize - size);
} else if(size > curSize) {
if (size > list.Capacity) {
list.Capacity = size;
}
list.AddRange(Enumerable.Repeat(value, size - curSize));
}
int remains = Math.Min(curSize, size);
for (int i = 0; i < remains; ++i) {
list[i] = value;
}
}
}
}

80
Runtime/ui/txt/layout.cs


using System;
using UnityEngine;
using Unity.UIWidgets.foundation;
namespace Unity.UIWidgets.ui {

List<float> _positions = new List<float>();
float _advance;
Rect _bounds;
string _text;
public static float measureText(float offset, string buf, int start, int count, TextStyle style,
public static float measureText(float offset, TextBuff buff, int start, int count, TextStyle style,
layout.doLayout(offset, buf, start, count, style);
layout.doLayout(offset, buff, start, count, style);
if (advances != null) {
var layoutAdv = layout.getAdvances();
for (int i = 0; i < count; i++) {

return layout.getAdvance();
}
public void doLayout(float offset, string text, int start, int count, TextStyle style) {
this._text = text;
this._advances.Clear();
this._positions.Clear();
public void doLayout(float offset, TextBuff buff, int start, int count, TextStyle style) {
this._start = start;
this._advances.resize(count, 0);
this._positions.resize(count, 0);
this._advance = 0;
this._bounds = null;
int wordstart = start == buff.size
? start
: LayoutUtils.getPrevWordBreakForCache(buff, start + 1);
int wordend;
for (int iter = start; iter < start + count; iter = wordend) {
wordend = LayoutUtils.getNextWordBreakForCache(buff, iter);
int wordCount = Math.Min(start + count, wordend) - iter;
this.layoutWord(offset, iter - start, buff.subBuff(wordstart, wordend - wordstart),
iter - wordstart, wordCount, style);
wordstart = wordend;
}
this._count = count;
}
void layoutWord(float offset, int layoutOffset,
TextBuff buff, int start, int wordCount, TextStyle style) {
float wordSpacing =
wordCount == 1 && LayoutUtils.isWordSpace(buff.charAt(start)) ? style.wordSpacing : 0;
font.RequestCharactersInTextureSafe(this._text.Substring(start, count),
font.RequestCharactersInTextureSafe(buff.subBuff(start, wordCount).getString(),
float x = this._advance;
float letterSpace = style.letterSpacing;
float letterSpaceHalfLeft = letterSpace * 0.5f;
float letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft;
for (int i = 0; i < wordCount; i++) {
var ch = buff.charAt(start + i);
if (i == 0) {
x += letterSpaceHalfLeft + wordSpacing;
this._advances[i + layoutOffset] += letterSpaceHalfLeft + wordSpacing;
}
else {
this._advances[i - 1 + layoutOffset] += letterSpaceHalfRight;
this._advances[i + layoutOffset] += letterSpaceHalfLeft;
x += letterSpace;
}
this._advance = 0;
this._bounds = null;
for (int i = 0; i < count; i++) {
int charIndex = start + i;
var ch = text[charIndex];
rect = rect.translate(this._advance, 0);
rect = rect.translate(x, 0);
if (this._bounds == null || this._bounds.isEmpty) {
this._bounds = rect;
}

this._positions.Add(this._advance);
this._positions[i + layoutOffset] = x;
this._advances.Add(advance);
this._advance += advance;
x += advance;
this._advances[i + layoutOffset] += advance;
if (i + 1 == wordCount) {
this._advances[i + layoutOffset] += letterSpaceHalfRight;
x += letterSpaceHalfRight;
}
this._advance = x;
}
public void setTabStops(TabStops tabStops) {

57
Runtime/ui/txt/layout_utils.cs


namespace Unity.UIWidgets.ui {
public static class LayoutUtils {
static class LayoutUtils {
public static bool isWordSpace(char ch) {
public static bool isWordSpace(ushort ch) {
return ch == ' ' || ch == CHAR_NBSP;
}

}
public static int getPrevWordBreakForCache(TextBuff buff, int offset) {
int len = buff.size;
if (offset == 0) {
return 0;
}
if (offset > len) {
offset = len;
}
if (isWordBreakBefore(buff.charAt(offset - 1))) {
return offset - 1;
}
for (int i = offset - 1; i > 0; i--) {
if (isWordBreakBefore(buff.charAt(i)) || isWordBreakAfter(buff.charAt(i - 1))) {
return i;
}
}
return 0;
}
public static int getNextWordBreakForCache(TextBuff buff, int offset) {
int len = buff.size;
if (offset >= len) {
return len;
}
if (isWordBreakAfter(buff.charAt(offset))) {
return offset + 1;
}
for (int i = offset + 1; i < len; i++) {
if (isWordBreakBefore(buff.charAt(i))) {
return i;
}
}
return len;
}
public static bool isWordBreakAfter(ushort c) {
if (isWordSpace(c) || (c >= 0x2000 && c <= 0x200a) || c == 0x3000) {
// spaces
return true;
}
return false;
}
public static bool isWordBreakBefore(ushort c) {
return isWordBreakAfter(c) || (c >= 0x3400 && c <= 0x9fff);
}
}
}

16
Runtime/ui/txt/linebreaker.cs


const float ScoreInfty = float.MaxValue;
const float ScoreDesperate = 1e10f;
string _textBuf;
int _textOffset;
int _textLength;
TextBuff _textBuf;
List<float> _charWidths = new List<float>();
List<int> _breaks = new List<int>();
List<float> _widths = new List<float>();

TabStops _tabStops;
int mFirstTabIndex;
List<Candidate> _candidates = new List<Candidate>();
public int computeBreaks() {
int nCand = this._candidates.Count;
if (nCand > 0 && (nCand == 1 || this._lastBreak != nCand - 1)) {

}
public void setText(string text, int textOffset, int textLength) {
this._textBuf = text;
this._textOffset = textOffset;
this._textLength = textLength;
this._wordBreaker.setText(this._textBuf, textOffset, textLength);
this._textBuf = new TextBuff(text, textOffset, textLength);
this._wordBreaker.setText(this._textBuf);
this._wordBreaker.next();
this._candidates.Clear();
Candidate can = new Candidate {

float width = 0.0f;
if (style != null) {
width = Layout.measureText(this._width - this._preBreak, this._textBuf,
start + this._textOffset, end - start, style,
start, end - start, style,
this._charWidths, start, this._tabStops);
}

int postSpaceCount = this._spaceCount;
for (int i = start; i < end; i++) {
char c = this._textBuf[i + this._textOffset];
char c = this._textBuf.charAt(i);
if (c == '\t') {
this._width = this._preBreak + this._tabStops.nextTab((this._width - this._preBreak));
if (this.mFirstTabIndex == int.MaxValue) {

10
Runtime/ui/txt/paragraph.cs


if (!string.IsNullOrEmpty(ellipsis) && !this._width.isInfinite() && !lineRange.hardBreak
&& i == lineRuns.Count - 1 && (lineNumber == lineLimit - 1 || this._paragraphStyle.maxLines == null)) {
float ellipsisWidth = Layout.measureText(runXOffset, ellipsis, 0,
float ellipsisWidth = Layout.measureText(runXOffset, new TextBuff(ellipsis), 0,
float textWidth = Layout.measureText(runXOffset, this._text, textStart, textCount,
float textWidth = Layout.measureText(runXOffset, new TextBuff(text), textStart, textCount,
run.style, textAdvances, 0, this._tabStops);
int truncateCount = 0;

}
}
layout.doLayout(runXOffset, text, textStart, textCount, run.style);
layout.doLayout(runXOffset, new TextBuff(text), textStart, textCount, run.style);
// var layoutAdvances = layout.getAdvances();
builder.setBounds(layout.getBounds());
builder.setBounds(layout.getBounds().translate(-layout.getX(0), 0)); // bounds relative to first character
glyphPositions.Clear();
for (int glyphIndex = 0; glyphIndex < textCount; ++glyphIndex) {

8
Runtime/ui/txt/word_separate.cs


internal static characterType classifyChar(string text, int index) {
if (char.IsWhiteSpace(text, index)) {
return classifyChar(text[index]);
}
internal static characterType classifyChar(char ch) {
if (char.IsWhiteSpace(ch)) {
if (char.IsLetterOrDigit(text, index) || text[index] == '\'') {
if (char.IsLetterOrDigit(ch) || ch == '\'') {
return characterType.LetterLike;
}

48
Runtime/ui/txt/wordbreaker.cs


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

return this._current;
}
public void setText(string data, int offset, int size) {
this._text = data;
this._offset = offset;
this._size = size;
public void setText(TextBuff text) {
this._text = text;
this._last = 0;
this._current = 0;
this._scanOffset = 0;

}
int _findNextBoundaryNormal() {
if (this._current == this._size) {
if (this._current == this._text.size) {
WordSeparate.characterType preType = WordSeparate.classifyChar(this._text, this._current + this._offset);
bool preBoundaryChar = isBoundaryChar(this._text[this._current + this._offset]);
WordSeparate.characterType preType = WordSeparate.classifyChar(this._text.charAt(this._current));
bool preBoundaryChar = isBoundaryChar(this._text.charAt(this._current));
for (; this._current < this._size; ++this._current) {
for (; this._current < this._text.size; ++this._current) {
if (this._current >= this._size) {
if (this._current >= this._text.size) {
if (isBoundaryChar(this._text[this._current + this._offset])) {
if (isBoundaryChar(this._text.charAt(this._current))) {
var currentType = WordSeparate.classifyChar(this._text, this._current + this._offset);
var currentType = WordSeparate.classifyChar(this._text.charAt(this._current));
if ((currentType == WordSeparate.characterType.WhiteSpace)
!= (preType == WordSeparate.characterType.WhiteSpace)) {
break;

void _detectEmailOrUrl() {
}
static uint nextCode(string text, ref int index, int end) {
uint ch = text[index++];
static uint nextCode(TextBuff text, ref int index, int end) {
uint ch = text.charAt(index);
index++;
if (index < end && isTrailSurrogate(text[index])) {
char ch2 = text[index];
if (index < end && isTrailSurrogate(text.charAt(index))) {
char ch2 = text.charAt(index);
index++;
ch = getSupplementary(ch, ch2);
}

}
static uint preCode(string text, ref int index, int start) {
uint ch = text[--index];
static uint preCode(TextBuff text, ref int index, int start) {
--index;
uint ch = text.charAt(index);
if (index > start && isLeadSurrogate(text[index - 1])) {
ch = getSupplementary(text[index - 1], ch);
if (index > start && isLeadSurrogate(text.charAt(index - 1))) {
ch = getSupplementary(text.charAt(index - 1), ch);
--index;
}
}

}
void nextUntilCodePoint() {
while (this._current < this._size
&& (char.IsLowSurrogate(this._text[this._current + this._offset])
|| char.IsHighSurrogate(this._text[this._current + this._offset]))) {
while (this._current < this._text.size
&& (char.IsLowSurrogate(this._text.charAt(this._current))
|| char.IsHighSurrogate(this._text.charAt(this._current)))) {
this._current++;
}
}

4
Samples/UIWidgetSample/txt/TextStyleSample.cs


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)})),
new Text("positive letter spacing", style: new TextStyle(fontSize: 14, letterSpacing:5)),
new Text("negative letter spacing", style: new TextStyle(fontSize: 14, letterSpacing:-1)),
new Text("positive word spacing test", style: new TextStyle(fontSize: 14, wordSpacing: 20f)),
new Text("negative word spacing test", style: new TextStyle(fontSize: 14, wordSpacing: -4f)),
};
return new Scaffold(

30
Runtime/ui/txt/text_buff.cs


using System;
using Unity.UIWidgets.foundation;
namespace Unity.UIWidgets.ui {
class TextBuff {
public readonly string text;
public readonly int offset;
public readonly int size;
public TextBuff(string text, int? offset = null, int? size = null) {
this.text = text;
this.offset = offset ?? 0;
this.size = size ?? text.Length - this.offset;
}
public char charAt(int index) {
return this.text[this.offset + index];
}
public TextBuff subBuff(int shift, int size) {
D.assert(shift >= 0 && shift <= this.size);
D.assert(shift + size <= this.size);
return new TextBuff(this.text, this.offset + shift, size);
}
public String getString() {
return this.text.Substring(this.offset, this.size);
}
}
}
正在加载...
取消
保存