您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

284 行
12 KiB

using UnityEngine;
namespace Unity.UIWidgets.ui {
static class Layout {
// Measure the length of the span of the text. Currently, this is only used to compute the length
// of ellipsis, assuming that the ellipsis does not contain any tab, tab is not considered for simplicity
public static float measureText(string text, TextStyle style) {
char startingChar = text[0];
float totalWidth = 0;
if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar)) {
float advance = style.fontSize + style.letterSpacing;
for (int i = 0; i < text.Length; i++) {
char ch = text[i];
if (char.IsHighSurrogate(ch) || EmojiUtils.isSingleCharNonEmptyEmoji(ch)) {
totalWidth += advance;
}
}
}
else {
Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font;
font.RequestCharactersInTextureSafe(text, style.UnityFontSize, style.UnityFontStyle);
for (int i = 0; i < text.Length; i++) {
char ch = text[i];
if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) {
totalWidth += glyphInfo.advance + style.letterSpacing;
}
else {
totalWidth += style.letterSpacing;
}
if (LayoutUtils.isWordSpace(ch)) {
totalWidth += style.wordSpacing;
}
}
}
return totalWidth;
}
public static int computeTruncateCount(float offset, string text, int start, int count, TextStyle style,
float advanceLimit, TabStops tabStops) {
char startingChar = text[start];
float currentAdvance = offset;
if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar)) {
float advance = style.fontSize + style.letterSpacing;
for (int i = 0; i < count; i++) {
char ch = text[start + i];
if (char.IsHighSurrogate(ch) || EmojiUtils.isSingleCharNonEmptyEmoji(ch)) {
currentAdvance += advance;
if (currentAdvance > advanceLimit) {
return count - i;
}
}
}
}
else {
Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font;
for (int i = 0; i < count; i++) {
char ch = text[start + i];
if (ch == '\t') {
currentAdvance = tabStops.nextTab(currentAdvance);
}
else if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) {
currentAdvance += glyphInfo.advance + style.letterSpacing;
}
else {
currentAdvance += style.letterSpacing;
}
if (LayoutUtils.isWordSpace(ch)) {
currentAdvance += style.wordSpacing;
}
if (currentAdvance > advanceLimit) {
return count - i;
}
}
}
return 0;
}
public static float computeCharWidths(float offset, string text, int start, int count, TextStyle style,
float[] advances, int advanceOffset, TabStops tabStops) {
char startingChar = text[start];
float totalWidths = 0;
if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar)) {
float advance = style.fontSize + style.letterSpacing;
for (int i = 0; i < count; i++) {
char ch = text[start + i];
if (char.IsHighSurrogate(ch) || EmojiUtils.isSingleCharNonEmptyEmoji(ch)) {
advances[i + advanceOffset] = advance;
totalWidths += advance;
}
else {
advances[i + advanceOffset] = 0;
}
}
}
else {
Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font;
// TODO: it is kind of a waste to require the entire string for this style, but SubString causes alloc
font.RequestCharactersInTextureSafe(text, style.UnityFontSize, style.UnityFontStyle);
for (int i = 0; i < count; i++) {
char ch = text[start + i];
if (ch == '\t') {
advances[i + advanceOffset] = tabStops.nextTab(offset + totalWidths) - (offset + totalWidths);
}
else if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) {
advances[i + advanceOffset] = glyphInfo.advance + style.letterSpacing;
}
else {
advances[i + advanceOffset] = style.letterSpacing;
}
if (LayoutUtils.isWordSpace(ch)) {
advances[i + advanceOffset] += style.wordSpacing;
}
totalWidths += advances[i + advanceOffset];
}
}
return totalWidths;
}
public static float doLayout(float offset, string text, int start, int count, TextStyle style,
float[] advances, float[] positions, TabStops tabStops, out UnityEngine.Rect bounds) {
float advance = 0;
Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font;
char startingChar = text[start];
bounds = new UnityEngine.Rect();
if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar)) {
advance = _layoutEmoji(text, start, count, style, font, advances, positions, ref bounds);
}
else {
// According to the logic of Paragraph.layout, it is assured that all the characters are requested
// in the texture before (in computing line breaks), so skip it here for optimization.
// The only exception is the ellipsis, which did not appear in line breaking. It is taken care of
// only when needed.
// font.RequestCharactersInTextureSafe(buff.text, style.UnityFontSize, style.UnityFontStyle);
// int wordstart = start == buff.size
// ? start
// : LayoutUtils.getPrevWordBreakForCache(buff, start + 1);
int wordend;
for (int iter = start; iter < start + count; iter = wordend) {
wordend = LayoutUtils.getNextWordBreak(text, iter, start + count);
advance = _layoutWord(offset, iter - start, text, iter,
wordend - iter, style, font, advances, positions, advance,
tabStops, ref bounds);
}
}
// bounds relative to first character
bounds.x -= positions[0];
return advance;
}
static float _layoutWord(float offset, int layoutOffset,
string text, int start, int wordCount, TextStyle style, Font font, float[] advances,
float[] positions, float initAdvance, TabStops tabStops, ref UnityEngine.Rect bounds) {
float wordSpacing =
wordCount == 1 && LayoutUtils.isWordSpace(text[start]) ? style.wordSpacing : 0;
float x = initAdvance;
float letterSpace = style.letterSpacing;
float letterSpaceHalfLeft = letterSpace * 0.5f;
float letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft;
for (int i = 0; i < wordCount; i++) {
initAdvance = x;
var ch = text[start + i];
if (i == 0) {
x += letterSpaceHalfLeft + wordSpacing;
advances[i + layoutOffset] = letterSpaceHalfLeft + wordSpacing;
}
else {
advances[i - 1 + layoutOffset] += letterSpaceHalfRight;
advances[i + layoutOffset] = letterSpaceHalfLeft;
x += letterSpace;
}
if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) {
_updateBounds(glyphInfo, x, ref bounds);
}
positions[i + layoutOffset] = x;
float advance;
if (ch == '\t') {
advance = tabStops.nextTab(initAdvance + offset) - initAdvance - offset;
}
else {
advance = glyphInfo.advance;
}
x += advance;
advances[i + layoutOffset] += advance;
if (i + 1 == wordCount) {
advances[i + layoutOffset] += letterSpaceHalfRight;
x += letterSpaceHalfRight;
}
}
return x;
}
static float _layoutEmoji(string text, int start, int count, TextStyle style, Font font, float[] advances,
float[] positions, ref UnityEngine.Rect bounds) {
var metrics = FontMetrics.fromFont(font, style.UnityFontSize);
float x = 0;
for (int i = 0; i < count; i++) {
char c = text[start + i];
if (EmojiUtils.isSingleCharNonEmptyEmoji(c) || char.IsHighSurrogate(c)) {
float letterSpace = style.letterSpacing;
float letterSpaceHalfLeft = letterSpace * 0.5f;
float letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft;
x += letterSpaceHalfLeft;
advances[i] = letterSpaceHalfLeft;
var minX = x;
var maxX = metrics.descent - metrics.ascent + x;
var minY = metrics.ascent;
var maxY = metrics.descent;
_updateBounds(minX, maxX, minY, maxY, ref bounds);
positions[i] = x;
float advance = style.fontSize;
x += advance;
advances[i] += advance;
advances[i] += letterSpaceHalfRight;
x += letterSpaceHalfRight;
}
else {
advances[i] = 0;
positions[i] = x;
}
}
return x;
}
static void _updateBounds(CharacterInfo glyphInfo, float x, ref UnityEngine.Rect bounds) {
var minX = glyphInfo.minX + x;
var maxX = glyphInfo.maxX + x;
var minY = -glyphInfo.maxY;
var maxY = -glyphInfo.minY;
_updateBounds(minX, maxX, minY, maxY, ref bounds);
}
static void _updateBounds(float minX, float maxX, float minY, float maxY, ref UnityEngine.Rect bounds) {
if (bounds.width <= 0 || bounds.height <= 0) {
bounds.Set(minX, minY, maxX - minX, maxY - minY);
}
else {
if (minX < bounds.x) {
bounds.x = minX;
}
if (minY < bounds.y) {
bounds.y = minY;
}
if (maxX > bounds.xMax) {
bounds.xMax = maxX;
}
if (maxY > bounds.yMax) {
bounds.yMax = maxY;
}
}
}
}
}