Skip to content
This repository was archived by the owner on Apr 29, 2021. It is now read-only.

Commit 4badda7

Browse files
committed
letter&word spacing
1 parent d63f9e6 commit 4badda7

File tree

9 files changed

+205
-65
lines changed

9 files changed

+205
-65
lines changed

Runtime/foundation/basic_types.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,5 +132,22 @@ public static string toStringList<T>(this IList<T> it) {
132132
}
133133
return "{ " + string.Join(", ", it.Select(item => item.ToString())) + " }";
134134
}
135+
136+
public static void resize<T>(this List<T> list, int size, T value) {
137+
int curSize = list.Count;
138+
if (size < curSize) {
139+
list.RemoveRange(size, curSize - size);
140+
} else if(size > curSize) {
141+
if (size > list.Capacity) {
142+
list.Capacity = size;
143+
}
144+
list.AddRange(Enumerable.Repeat(value, size - curSize));
145+
}
146+
147+
int remains = Math.Min(curSize, size);
148+
for (int i = 0; i < remains; ++i) {
149+
list[i] = value;
150+
}
151+
}
135152
}
136153
}

Runtime/ui/txt/layout.cs

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
using System;
12
using System.Collections.Generic;
2-
using UnityEngine;
3+
using Unity.UIWidgets.foundation;
34

45
namespace Unity.UIWidgets.ui {
56

@@ -10,15 +11,14 @@ class Layout {
1011
List<float> _positions = new List<float>();
1112
float _advance;
1213
Rect _bounds;
13-
string _text;
1414
TabStops _tabStops;
1515

1616

17-
public static float measureText(float offset, string buf, int start, int count, TextStyle style,
17+
public static float measureText(float offset, TextBuff buff, int start, int count, TextStyle style,
1818
List<float> advances, int advanceOffset, TabStops tabStops) {
1919
Layout layout = new Layout();
2020
layout.setTabStops(tabStops);
21-
layout.doLayout(offset, buf, start, count, style);
21+
layout.doLayout(offset, buff, start, count, style);
2222
if (advances != null) {
2323
var layoutAdv = layout.getAdvances();
2424
for (int i = 0; i < count; i++) {
@@ -29,41 +29,79 @@ public static float measureText(float offset, string buf, int start, int count,
2929
return layout.getAdvance();
3030
}
3131

32-
public void doLayout(float offset, string text, int start, int count, TextStyle style) {
33-
this._text = text;
34-
this._advances.Clear();
35-
this._positions.Clear();
32+
public void doLayout(float offset, TextBuff buff, int start, int count, TextStyle style) {
33+
this._start = start;
3634
this._count = count;
35+
this._advances.resize(count, 0);
36+
this._positions.resize(count, 0);
37+
this._advance = 0;
38+
this._bounds = null;
39+
40+
int wordstart = start == buff.size
41+
? start
42+
: LayoutUtils.getPrevWordBreakForCache(buff, start + 1);
43+
int wordend;
44+
for (int iter = start; iter < start + count; iter = wordend) {
45+
wordend = LayoutUtils.getNextWordBreakForCache(buff, iter);
46+
int wordCount = Math.Min(start + count, wordend) - iter;
47+
this.layoutWord(offset, iter - start, buff.subBuff(wordstart, wordend - wordstart),
48+
iter - wordstart, wordCount, style);
49+
wordstart = wordend;
50+
}
51+
this._count = count;
52+
53+
}
54+
55+
void layoutWord(float offset, int layoutOffset,
56+
TextBuff buff, int start, int wordCount, TextStyle style) {
57+
float wordSpacing =
58+
wordCount == 1 && LayoutUtils.isWordSpace(buff.charAt(start)) ? style.wordSpacing : 0;
59+
3760
var font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font;
38-
font.RequestCharactersInTextureSafe(this._text.Substring(start, count),
61+
font.RequestCharactersInTextureSafe(buff.subBuff(start, wordCount).getString(),
3962
style.UnityFontSize,
4063
style.UnityFontStyle);
64+
float x = this._advance;
65+
float letterSpace = style.letterSpacing;
66+
float letterSpaceHalfLeft = letterSpace * 0.5f;
67+
float letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft;
68+
69+
for (int i = 0; i < wordCount; i++) {
70+
var ch = buff.charAt(start + i);
71+
if (i == 0) {
72+
x += letterSpaceHalfLeft + wordSpacing;
73+
this._advances[i + layoutOffset] += letterSpaceHalfLeft + wordSpacing;
74+
}
75+
else {
76+
this._advances[i - 1 + layoutOffset] += letterSpaceHalfRight;
77+
this._advances[i + layoutOffset] += letterSpaceHalfLeft;
78+
x += letterSpace;
79+
}
4180

42-
this._advance = 0;
43-
this._bounds = null;
44-
for (int i = 0; i < count; i++) {
45-
int charIndex = start + i;
46-
var ch = text[charIndex];
4781
var glyphInfo = font.getGlyphInfo(ch, style.UnityFontSize, style.UnityFontStyle);
48-
4982
var rect = glyphInfo.rect;
50-
rect = rect.translate(this._advance, 0);
83+
rect = rect.translate(x, 0);
5184
if (this._bounds == null || this._bounds.isEmpty) {
5285
this._bounds = rect;
5386
}
5487
else {
5588
this._bounds = this._bounds.expandToInclude(rect);
5689
}
57-
58-
this._positions.Add(this._advance);
90+
91+
this._positions[i + layoutOffset] = x;
5992
float advance = glyphInfo.advance;
6093
if (ch == '\t') {
6194
advance = this._tabStops.nextTab((this._advance + offset)) - this._advance;
6295
}
63-
64-
this._advances.Add(advance);
65-
this._advance += advance;
96+
x += advance;
97+
this._advances[i + layoutOffset] += advance;
98+
if (i + 1 == wordCount) {
99+
this._advances[i + layoutOffset] += letterSpaceHalfRight;
100+
x += letterSpaceHalfRight;
101+
}
66102
}
103+
104+
this._advance = x;
67105
}
68106

69107
public void setTabStops(TabStops tabStops) {

Runtime/ui/txt/layout_utils.cs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,67 @@
11
namespace Unity.UIWidgets.ui {
2-
public static class LayoutUtils {
2+
static class LayoutUtils {
33
public const char CHAR_NBSP = '\u00A0';
44

5-
public static bool isWordSpace(char ch) {
5+
public static bool isWordSpace(ushort ch) {
66
return ch == ' ' || ch == CHAR_NBSP;
77
}
88

99
public static bool isLineEndSpace(char c) {
1010
return c == '\n' || c == ' ' || c == 0x1680 || (0x2000 <= c && c <= 0x200A && c != 0x2007) ||
1111
c == 0x205F || c == 0x3000;
1212
}
13+
14+
public static int getPrevWordBreakForCache(TextBuff buff, int offset) {
15+
int len = buff.size;
16+
if (offset == 0) {
17+
return 0;
18+
}
19+
20+
if (offset > len) {
21+
offset = len;
22+
}
23+
if (isWordBreakBefore(buff.charAt(offset - 1))) {
24+
return offset - 1;
25+
}
26+
for (int i = offset - 1; i > 0; i--) {
27+
if (isWordBreakBefore(buff.charAt(i)) || isWordBreakAfter(buff.charAt(i - 1))) {
28+
return i;
29+
}
30+
}
31+
return 0;
32+
}
33+
34+
35+
public static int getNextWordBreakForCache(TextBuff buff, int offset) {
36+
int len = buff.size;
37+
if (offset >= len) {
38+
return len;
39+
}
40+
41+
if (isWordBreakAfter(buff.charAt(offset))) {
42+
return offset + 1;
43+
}
44+
45+
for (int i = offset + 1; i < len; i++) {
46+
if (isWordBreakBefore(buff.charAt(i))) {
47+
return i;
48+
}
49+
}
50+
51+
return len;
52+
}
53+
54+
public static bool isWordBreakAfter(ushort c) {
55+
if (isWordSpace(c) || (c >= 0x2000 && c <= 0x200a) || c == 0x3000) {
56+
// spaces
57+
return true;
58+
}
59+
return false;
60+
}
61+
62+
public static bool isWordBreakBefore(ushort c) {
63+
return isWordBreakAfter(c) || (c >= 0x3400 && c <= 0x9fff);
64+
}
65+
1366
}
1467
}

Runtime/ui/txt/linebreaker.cs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,7 @@ class LineBreaker {
7171
const float ScoreInfty = float.MaxValue;
7272
const float ScoreDesperate = 1e10f;
7373

74-
string _textBuf;
75-
int _textOffset;
76-
int _textLength;
74+
TextBuff _textBuf;
7775
List<float> _charWidths = new List<float>();
7876
List<int> _breaks = new List<int>();
7977
List<float> _widths = new List<float>();
@@ -88,7 +86,7 @@ class LineBreaker {
8886
TabStops _tabStops;
8987
int mFirstTabIndex;
9088
List<Candidate> _candidates = new List<Candidate>();
91-
89+
9290
public int computeBreaks() {
9391
int nCand = this._candidates.Count;
9492
if (nCand > 0 && (nCand == 1 || this._lastBreak != nCand - 1)) {
@@ -110,10 +108,8 @@ public void resize(int size) {
110108
}
111109

112110
public void setText(string text, int textOffset, int textLength) {
113-
this._textBuf = text;
114-
this._textOffset = textOffset;
115-
this._textLength = textLength;
116-
this._wordBreaker.setText(this._textBuf, textOffset, textLength);
111+
this._textBuf = new TextBuff(text, textOffset, textLength);
112+
this._wordBreaker.setText(this._textBuf);
117113
this._wordBreaker.next();
118114
this._candidates.Clear();
119115
Candidate can = new Candidate {
@@ -136,7 +132,7 @@ public float addStyleRun(TextStyle style, int start, int end) {
136132
float width = 0.0f;
137133
if (style != null) {
138134
width = Layout.measureText(this._width - this._preBreak, this._textBuf,
139-
start + this._textOffset, end - start, style,
135+
start, end - start, style,
140136
this._charWidths, start, this._tabStops);
141137
}
142138

@@ -149,7 +145,7 @@ public float addStyleRun(TextStyle style, int start, int end) {
149145
int postSpaceCount = this._spaceCount;
150146

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

Runtime/ui/txt/paragraph.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -358,13 +358,13 @@ public void layout(ParagraphConstraints constraints) {
358358
if (!string.IsNullOrEmpty(ellipsis) && !this._width.isInfinite() && !lineRange.hardBreak
359359
&& i == lineRuns.Count - 1 && (lineNumber == lineLimit - 1 || this._paragraphStyle.maxLines == null)) {
360360

361-
float ellipsisWidth = Layout.measureText(runXOffset, ellipsis, 0,
361+
float ellipsisWidth = Layout.measureText(runXOffset, new TextBuff(ellipsis), 0,
362362
ellipsis.Length, run.style, null, 0, this._tabStops);
363363
List<float> textAdvances = new List<float>(textCount);
364364
for (int index = 0; index < textCount;++index) {
365365
textAdvances.Add(0);
366366
}
367-
float textWidth = Layout.measureText(runXOffset, this._text, textStart, textCount,
367+
float textWidth = Layout.measureText(runXOffset, new TextBuff(text), textStart, textCount,
368368
run.style, textAdvances, 0, this._tabStops);
369369

370370
int truncateCount = 0;
@@ -385,15 +385,15 @@ public void layout(ParagraphConstraints constraints) {
385385
}
386386
}
387387

388-
layout.doLayout(runXOffset, text, textStart, textCount, run.style);
388+
layout.doLayout(runXOffset, new TextBuff(text), textStart, textCount, run.style);
389389
if (layout.nGlyphs() == 0) {
390390
continue;
391391
}
392392

393393
float wordStartPosition = float.NaN;
394-
// var layoutAdvances = layout.getAdvances();
395394
builder.allocRunPos(run.style, text, textStart, textCount);
396-
builder.setBounds(layout.getBounds());
395+
builder.setBounds(layout.getBounds().translate(-layout.getX(0), 0)); // bounds relative to first character
396+
397397
glyphPositions.Clear();
398398

399399
for (int glyphIndex = 0; glyphIndex < textCount; ++glyphIndex) {

Runtime/ui/txt/text_buff.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System;
2+
using Unity.UIWidgets.foundation;
3+
4+
namespace Unity.UIWidgets.ui {
5+
class TextBuff {
6+
public readonly string text;
7+
public readonly int offset;
8+
public readonly int size;
9+
10+
public TextBuff(string text, int? offset = null, int? size = null) {
11+
this.text = text;
12+
this.offset = offset ?? 0;
13+
this.size = size ?? text.Length - this.offset;
14+
}
15+
16+
public char charAt(int index) {
17+
return this.text[this.offset + index];
18+
}
19+
20+
public TextBuff subBuff(int shift, int size) {
21+
D.assert(shift >= 0 && shift <= this.size);
22+
D.assert(shift + size <= this.size);
23+
return new TextBuff(this.text, this.offset + shift, size);
24+
}
25+
26+
public String getString() {
27+
return this.text.Substring(this.offset, this.size);
28+
}
29+
}
30+
}

Runtime/ui/txt/word_separate.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,15 @@ public Range<int> findWordRange(int index) {
5050

5151

5252
internal static characterType classifyChar(string text, int index) {
53-
if (char.IsWhiteSpace(text, index)) {
53+
return classifyChar(text[index]);
54+
}
55+
56+
internal static characterType classifyChar(char ch) {
57+
if (char.IsWhiteSpace(ch)) {
5458
return characterType.WhiteSpace;
5559
}
5660

57-
if (char.IsLetterOrDigit(text, index) || text[index] == '\'') {
61+
if (char.IsLetterOrDigit(ch) || ch == '\'') {
5862
return characterType.LetterLike;
5963
}
6064

0 commit comments

Comments
 (0)