Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
c30bb7b85This file is pinned to the selected release. Download the original to open it in your design tool.
/******************************************************************************
* Copyright (C) Leap Motion, Inc. 2011-2018. *
* Leap Motion proprietary and confidential. *
* *
* Use subject to the terms of the Leap Motion SDK Agreement available at *
* https://developer.leapmotion.com/sdk_agreement, or another agreement *
* between Leap Motion and you, your company or other organization. *
******************************************************************************/
using System;
using System.Collections.Generic;
namespace Leap.Unity.GraphicalRenderer {
public static class TextWrapper {
/// <summary>
/// References a contiguous sequence of characters in a string.
/// </summary>
public struct Token {
public int start;
public int end;
public int length {
get {
return end - start;
}
set {
end = start + value;
}
}
public bool IsNewline(string source) {
return source[start] == '\n';
}
public bool IsWhitespace(string source) {
return char.IsWhiteSpace(source[start]);
}
public float GetWidth(string source, Func<char, float> charWidth) {
float width = 0;
for (int i = 0; i < length; i++) {
width += charWidth(source[i + start]);
}
return width;
}
}
/// <summary>
/// References a contiguous sequence of characters in a string
/// </summary>
public struct Line {
public int start;
public int end;
public float width;
public int length {
get {
return end - start;
}
set {
end = start + value;
}
}
public void TrimEnd(string source, Func<char, float> charWidth) {
while (length > 0) {
char end = source[start + length - 1];
if (char.IsWhiteSpace(end)) {
width -= charWidth(end);
length--;
} else {
return;
}
}
}
}
/// <summary>
/// Given a string, turn it into a list of tokens. A token is either:
/// - A single whitespace character
/// - A collection of non-whitespace characters
/// This algorithm will never create more than one token from a contiguous
/// section of non-whitespace tokens.
/// </summary>
public static void Tokenize(string text, List<Token> tokens) {
int index = 0;
int textLength = text.Length;
while (true) {
while (true) {
if (index >= textLength) {
return;
}
if (!char.IsWhiteSpace(text[index])) {
break;
}
tokens.Add(new Token() {
start = index,
length = 1
});
index++;
}
Token token = new Token() {
start = index
};
index++;
while (index < textLength && !char.IsWhiteSpace(text[index])) {
index++;
}
token.length = index - token.start;
tokens.Add(token);
}
}
/// <summary>
/// Given a list of tokens, turn it into a list of lines.
/// The lines will satisfy the following properties:
/// - No line will be longer than the maxLineWidth
/// - No token will be split unless it is both
/// - the first token on a line
/// - too long to fit on the line
/// - No line will contain any newline characters
///
/// Note that width is not measured in character count, but in
/// character width provided by the given delegate
/// </summary>
public static void Wrap(string source, List<Token> tokens, List<Line> lines, Func<char, float> widthFunc, float maxLineWidth) {
if (tokens.Count == 0) {
return;
}
int tokenIndex = 0;
Token token = tokens[0];
while (true) {
if (token.IsNewline(source)) {
lines.Add(new Line() {
start = token.start,
length = 0,
width = 0
});
goto LOOP_END;
}
NEW_LINE_CONTINUE_TOKEN:
float firstTokenWidth = widthFunc(source[token.start]);
//Line starts out equal to the first character of the first token
Line line = new Line() {
start = token.start,
end = token.end,
width = firstTokenWidth
};
//Add as many of the rest of the characters of the token as we can
for (int i = token.start + 1; i < token.end; i++) {
float charWidth = widthFunc(source[i]);
//If the line gets too big, we are forced to truncate!
if (firstTokenWidth + charWidth > maxLineWidth) {
line.end = i;
line.width = firstTokenWidth;
lines.Add(line);
token.start = i;
//Start a new line with the remainder of the current token
goto NEW_LINE_CONTINUE_TOKEN;
}
firstTokenWidth += charWidth;
}
//Set line equal to the first token
line.width = firstTokenWidth;
line.length = token.length;
//Move to the next token to begin adding them to the line
tokenIndex++;
if (tokenIndex >= tokens.Count) {
//Line only contains first token, which cannot be whitespace, so we don't trim
lines.Add(line);
return;
}
token = tokens[tokenIndex];
//Fit the rest of the tokens on this line
while (true) {
//If the token is a newline, simply add the line and finish the main loop
if (token.IsNewline(source)) {
line.TrimEnd(source, widthFunc);
lines.Add(line);
break;
}
//If a non-whitespace token is too big, finish the line
float tokenWidth = token.GetWidth(source, widthFunc);
if (line.width + tokenWidth > maxLineWidth && !token.IsWhitespace(source)) {
line.TrimEnd(source, widthFunc);
lines.Add(line);
//Go start a new line with the current token
goto NEW_LINE_CONTINUE_TOKEN;
}
//Otherwise append to the line and keep trying tokens
line.length += token.length;
line.width += tokenWidth;
tokenIndex++;
if (tokenIndex >= tokens.Count) {
lines.Add(line);
return;
}
token = tokens[tokenIndex];
}
LOOP_END:
tokenIndex++;
if (tokenIndex >= tokens.Count) {
return;
}
token = tokens[tokenIndex];
}
}
}
}