StringSplitter.java
net.minecraft.client.StringSplitter
信息
- 全限定名:net.minecraft.client.StringSplitter
- 类型:public class
- 包:net.minecraft.client
- 源码路径:src/main/java/net/minecraft/client/StringSplitter.java
- 起始行号:L22
- 职责:
TODO
字段/常量
widthProvider- 类型:
StringSplitter.WidthProvider - 修饰符:
private final - 源码定位:
L23 - 说明:
TODO
- 类型:
内部类/嵌套类型
-
net.minecraft.client.StringSplitter.FlatComponents- 类型:
class - 修饰符:
private static - 源码定位:
L244 - 说明:
TODO
- 类型:
-
net.minecraft.client.StringSplitter.LineBreakFinder- 类型:
class - 修饰符:
private - 源码定位:
L312 - 说明:
TODO
- 类型:
-
net.minecraft.client.StringSplitter.LineComponent- 类型:
class - 修饰符:
private static - 源码定位:
L379 - 说明:
TODO
- 类型:
-
net.minecraft.client.StringSplitter.LinePosConsumer- 类型:
interface - 修饰符:
public - 源码定位:
L401 - 说明:
TODO
- 类型:
-
net.minecraft.client.StringSplitter.WidthLimitedCharSink- 类型:
class - 修饰符:
private - 源码定位:
L406 - 说明:
TODO
- 类型:
-
net.minecraft.client.StringSplitter.WidthProvider- 类型:
interface - 修饰符:
public - 源码定位:
L438 - 说明:
TODO
- 类型:
构造器
public StringSplitter(StringSplitter.WidthProvider widthProvider) @ L25
- 构造器名:StringSplitter
- 源码定位:L25
- 修饰符:public
参数:
- widthProvider: StringSplitter.WidthProvider
说明:
TODO
方法
下面的方法块按源码顺序生成。
public float stringWidth(String str) @ L29
- 方法名:stringWidth
- 源码定位:L29
- 返回类型:float
- 修饰符:public
参数:
- str: String
说明:
TODO
public float stringWidth(FormattedText text) @ L42
- 方法名:stringWidth
- 源码定位:L42
- 返回类型:float
- 修饰符:public
参数:
- text: FormattedText
说明:
TODO
public float stringWidth(FormattedCharSequence text) @ L51
- 方法名:stringWidth
- 源码定位:L51
- 返回类型:float
- 修饰符:public
参数:
- text: FormattedCharSequence
说明:
TODO
public int plainIndexAtWidth(String str, int maxWidth, Style style) @ L60
- 方法名:plainIndexAtWidth
- 源码定位:L60
- 返回类型:int
- 修饰符:public
参数:
- str: String
- maxWidth: int
- style: Style
说明:
TODO
public String plainHeadByWidth(String str, int maxWidth, Style style) @ L66
- 方法名:plainHeadByWidth
- 源码定位:L66
- 返回类型:String
- 修饰符:public
参数:
- str: String
- maxWidth: int
- style: Style
说明:
TODO
public String plainTailByWidth(String str, int maxWidth, Style style) @ L70
- 方法名:plainTailByWidth
- 源码定位:L70
- 返回类型:String
- 修饰符:public
参数:
- str: String
- maxWidth: int
- style: Style
说明:
TODO
public FormattedText headByWidth(FormattedText text, int width, Style initialStyle) @ L85
- 方法名:headByWidth
- 源码定位:L85
- 返回类型:FormattedText
- 修饰符:public
参数:
- text: FormattedText
- width: int
- initialStyle: Style
说明:
TODO
public int findLineBreak(String input, int max, Style initialStyle) @ L116
- 方法名:findLineBreak
- 源码定位:L116
- 返回类型:int
- 修饰符:public
参数:
- input: String
- max: int
- initialStyle: Style
说明:
TODO
public static int getWordPosition(String text, int dir, int from, boolean stripSpaces) @ L122
- 方法名:getWordPosition
- 源码定位:L122
- 返回类型:int
- 修饰符:public static
参数:
- text: String
- dir: int
- from: int
- stripSpaces: boolean
说明:
TODO
public void splitLines(String input, int maxWidth, Style initialStyle, boolean includeAll, StringSplitter.LinePosConsumer output) @ L163
- 方法名:splitLines
- 源码定位:L163
- 返回类型:void
- 修饰符:public
参数:
- input: String
- maxWidth: int
- initialStyle: Style
- includeAll: boolean
- output: StringSplitter.LinePosConsumer
说明:
TODO
public List<FormattedText> splitLines(String input, int maxWidth, Style initialStyle) @ L185
- 方法名:splitLines
- 源码定位:L185
- 返回类型:List
- 修饰符:public
参数:
- input: String
- maxWidth: int
- initialStyle: Style
说明:
TODO
public List<FormattedText> splitLines(FormattedText input, int maxWidth, Style initialStyle) @ L191
- 方法名:splitLines
- 源码定位:L191
- 返回类型:List
- 修饰符:public
参数:
- input: FormattedText
- maxWidth: int
- initialStyle: Style
说明:
TODO
public void splitLines(FormattedText input, int maxWidth, Style initialStyle, BiConsumer<FormattedText,Boolean> output) @ L197
- 方法名:splitLines
- 源码定位:L197
- 返回类型:void
- 修饰符:public
参数:
- input: FormattedText
- maxWidth: int
- initialStyle: Style
- output: BiConsumer<FormattedText,Boolean>
说明:
TODO
代码
@OnlyIn(Dist.CLIENT)
public class StringSplitter {
private final StringSplitter.WidthProvider widthProvider;
public StringSplitter(StringSplitter.WidthProvider widthProvider) {
this.widthProvider = widthProvider;
}
public float stringWidth(@Nullable String str) {
if (str == null) {
return 0.0F;
} else {
MutableFloat result = new MutableFloat();
StringDecomposer.iterateFormatted(str, Style.EMPTY, (position, style, codepoint) -> {
result.add(this.widthProvider.getWidth(codepoint, style));
return true;
});
return result.floatValue();
}
}
public float stringWidth(FormattedText text) {
MutableFloat result = new MutableFloat();
StringDecomposer.iterateFormatted(text, Style.EMPTY, (position, style, codepoint) -> {
result.add(this.widthProvider.getWidth(codepoint, style));
return true;
});
return result.floatValue();
}
public float stringWidth(FormattedCharSequence text) {
MutableFloat result = new MutableFloat();
text.accept((position, style, codepoint) -> {
result.add(this.widthProvider.getWidth(codepoint, style));
return true;
});
return result.floatValue();
}
public int plainIndexAtWidth(String str, int maxWidth, Style style) {
StringSplitter.WidthLimitedCharSink output = new StringSplitter.WidthLimitedCharSink(maxWidth);
StringDecomposer.iterate(str, style, output);
return output.getPosition();
}
public String plainHeadByWidth(String str, int maxWidth, Style style) {
return str.substring(0, this.plainIndexAtWidth(str, maxWidth, style));
}
public String plainTailByWidth(String str, int maxWidth, Style style) {
MutableFloat currentWidth = new MutableFloat();
MutableInt result = new MutableInt(str.length());
StringDecomposer.iterateBackwards(str, style, (position, s, codepoint) -> {
float w = currentWidth.addAndGet(this.widthProvider.getWidth(codepoint, s));
if (w > maxWidth) {
return false;
} else {
result.setValue(position);
return true;
}
});
return str.substring(result.intValue());
}
public FormattedText headByWidth(FormattedText text, int width, Style initialStyle) {
final StringSplitter.WidthLimitedCharSink output = new StringSplitter.WidthLimitedCharSink(width);
return text.visit(new FormattedText.StyledContentConsumer<FormattedText>() {
private final ComponentCollector collector;
{
Objects.requireNonNull(StringSplitter.this);
this.collector = new ComponentCollector();
}
@Override
public Optional<FormattedText> accept(Style style, String contents) {
output.resetPosition();
if (!StringDecomposer.iterateFormatted(contents, style, output)) {
String partial = contents.substring(0, output.getPosition());
if (!partial.isEmpty()) {
this.collector.append(FormattedText.of(partial, style));
}
return Optional.of(this.collector.getResultOrEmpty());
} else {
if (!contents.isEmpty()) {
this.collector.append(FormattedText.of(contents, style));
}
return Optional.empty();
}
}
}, initialStyle).orElse(text);
}
public int findLineBreak(String input, int max, Style initialStyle) {
StringSplitter.LineBreakFinder finder = new StringSplitter.LineBreakFinder(max);
StringDecomposer.iterateFormatted(input, initialStyle, finder);
return finder.getSplitPosition();
}
public static int getWordPosition(String text, int dir, int from, boolean stripSpaces) {
int result = from;
boolean reverse = dir < 0;
int abs = Math.abs(dir);
for (int i = 0; i < abs; i++) {
if (reverse) {
while (stripSpaces && result > 0 && (text.charAt(result - 1) == ' ' || text.charAt(result - 1) == '\n')) {
result--;
}
while (result > 0 && text.charAt(result - 1) != ' ' && text.charAt(result - 1) != '\n') {
result--;
}
} else {
int length = text.length();
int index1 = text.indexOf(32, result);
int index2 = text.indexOf(10, result);
if (index1 == -1 && index2 == -1) {
result = -1;
} else if (index1 != -1 && index2 != -1) {
result = Math.min(index1, index2);
} else if (index1 != -1) {
result = index1;
} else {
result = index2;
}
if (result == -1) {
result = length;
} else {
while (stripSpaces && result < length && (text.charAt(result) == ' ' || text.charAt(result) == '\n')) {
result++;
}
}
}
}
return result;
}
public void splitLines(String input, int maxWidth, Style initialStyle, boolean includeAll, StringSplitter.LinePosConsumer output) {
int start = 0;
int size = input.length();
Style workStyle = initialStyle;
while (start < size) {
StringSplitter.LineBreakFinder finder = new StringSplitter.LineBreakFinder(maxWidth);
boolean endOfText = StringDecomposer.iterateFormatted(input, start, workStyle, initialStyle, finder);
if (endOfText) {
output.accept(workStyle, start, size);
break;
}
int lineBreak = finder.getSplitPosition();
char firstTailChar = input.charAt(lineBreak);
int adjustedBreak = firstTailChar != '\n' && firstTailChar != ' ' ? lineBreak : lineBreak + 1;
output.accept(workStyle, start, includeAll ? adjustedBreak : lineBreak);
start = adjustedBreak;
workStyle = finder.getSplitStyle();
}
}
public List<FormattedText> splitLines(String input, int maxWidth, Style initialStyle) {
List<FormattedText> result = Lists.newArrayList();
this.splitLines(input, maxWidth, initialStyle, false, (style, start, end) -> result.add(FormattedText.of(input.substring(start, end), style)));
return result;
}
public List<FormattedText> splitLines(FormattedText input, int maxWidth, Style initialStyle) {
List<FormattedText> result = Lists.newArrayList();
this.splitLines(input, maxWidth, initialStyle, (text, wrapped) -> result.add(text));
return result;
}
public void splitLines(FormattedText input, int maxWidth, Style initialStyle, BiConsumer<FormattedText, Boolean> output) {
List<StringSplitter.LineComponent> partList = Lists.newArrayList();
input.visit((style, contents) -> {
if (!contents.isEmpty()) {
partList.add(new StringSplitter.LineComponent(contents, style));
}
return Optional.empty();
}, initialStyle);
StringSplitter.FlatComponents parts = new StringSplitter.FlatComponents(partList);
boolean shouldRestart = true;
boolean forceNewLine = false;
boolean isWrapped = false;
while (shouldRestart) {
shouldRestart = false;
StringSplitter.LineBreakFinder finder = new StringSplitter.LineBreakFinder(maxWidth);
for (StringSplitter.LineComponent part : parts.parts) {
boolean endOfText = StringDecomposer.iterateFormatted(part.contents, 0, part.style, initialStyle, finder);
if (!endOfText) {
int lineBreak = finder.getSplitPosition();
Style lineBreakStyle = finder.getSplitStyle();
char firstTailChar = parts.charAt(lineBreak);
boolean isNewLine = firstTailChar == '\n';
boolean skipNextChar = isNewLine || firstTailChar == ' ';
forceNewLine = isNewLine;
FormattedText result = parts.splitAt(lineBreak, skipNextChar ? 1 : 0, lineBreakStyle);
output.accept(result, isWrapped);
isWrapped = !isNewLine;
shouldRestart = true;
break;
}
finder.addToOffset(part.contents.length());
}
}
FormattedText lastLine = parts.getRemainder();
if (lastLine != null) {
output.accept(lastLine, isWrapped);
} else if (forceNewLine) {
output.accept(FormattedText.EMPTY, false);
}
}
@OnlyIn(Dist.CLIENT)
private static class FlatComponents {
private final List<StringSplitter.LineComponent> parts;
private String flatParts;
public FlatComponents(List<StringSplitter.LineComponent> parts) {
this.parts = parts;
this.flatParts = parts.stream().map(p -> p.contents).collect(Collectors.joining());
}
public char charAt(int position) {
return this.flatParts.charAt(position);
}
public FormattedText splitAt(int skipPosition, int skipSize, Style splitStyle) {
ComponentCollector result = new ComponentCollector();
ListIterator<StringSplitter.LineComponent> it = this.parts.listIterator();
int position = skipPosition;
boolean inSkip = false;
while (it.hasNext()) {
StringSplitter.LineComponent element = it.next();
String contents = element.contents;
int contentsSize = contents.length();
if (!inSkip) {
if (position > contentsSize) {
result.append(element);
it.remove();
position -= contentsSize;
} else {
String beforeSplit = contents.substring(0, position);
if (!beforeSplit.isEmpty()) {
result.append(FormattedText.of(beforeSplit, element.style));
}
position += skipSize;
inSkip = true;
}
}
if (inSkip) {
if (position <= contentsSize) {
String afterSplit = contents.substring(position);
if (afterSplit.isEmpty()) {
it.remove();
} else {
it.set(new StringSplitter.LineComponent(afterSplit, splitStyle));
}
break;
}
it.remove();
position -= contentsSize;
}
}
this.flatParts = this.flatParts.substring(skipPosition + skipSize);
return result.getResultOrEmpty();
}
public @Nullable FormattedText getRemainder() {
ComponentCollector result = new ComponentCollector();
this.parts.forEach(result::append);
this.parts.clear();
return result.getResult();
}
}
@OnlyIn(Dist.CLIENT)
private class LineBreakFinder implements FormattedCharSink {
private final float maxWidth;
private int lineBreak;
private Style lineBreakStyle;
private boolean hadNonZeroWidthChar;
private float width;
private int lastSpace;
private Style lastSpaceStyle;
private int nextChar;
private int offset;
public LineBreakFinder(float maxWidth) {
Objects.requireNonNull(StringSplitter.this);
super();
this.lineBreak = -1;
this.lineBreakStyle = Style.EMPTY;
this.lastSpace = -1;
this.lastSpaceStyle = Style.EMPTY;
this.maxWidth = Math.max(maxWidth, 1.0F);
}
@Override
public boolean accept(int position, Style style, int codepoint) {
int adjustedPosition = position + this.offset;
switch (codepoint) {
case 10:
return this.finishIteration(adjustedPosition, style);
case 32:
this.lastSpace = adjustedPosition;
this.lastSpaceStyle = style;
default:
float charWidth = StringSplitter.this.widthProvider.getWidth(codepoint, style);
this.width += charWidth;
if (!this.hadNonZeroWidthChar || !(this.width > this.maxWidth)) {
this.hadNonZeroWidthChar |= charWidth != 0.0F;
this.nextChar = adjustedPosition + Character.charCount(codepoint);
return true;
} else {
return this.lastSpace != -1 ? this.finishIteration(this.lastSpace, this.lastSpaceStyle) : this.finishIteration(adjustedPosition, style);
}
}
}
private boolean finishIteration(int lineBreak, Style style) {
this.lineBreak = lineBreak;
this.lineBreakStyle = style;
return false;
}
private boolean lineBreakFound() {
return this.lineBreak != -1;
}
public int getSplitPosition() {
return this.lineBreakFound() ? this.lineBreak : this.nextChar;
}
public Style getSplitStyle() {
return this.lineBreakStyle;
}
public void addToOffset(int delta) {
this.offset += delta;
}
}
@OnlyIn(Dist.CLIENT)
private static class LineComponent implements FormattedText {
private final String contents;
private final Style style;
public LineComponent(String contents, Style style) {
this.contents = contents;
this.style = style;
}
@Override
public <T> Optional<T> visit(FormattedText.ContentConsumer<T> output) {
return output.accept(this.contents);
}
@Override
public <T> Optional<T> visit(FormattedText.StyledContentConsumer<T> output, Style parentStyle) {
return output.accept(this.style.applyTo(parentStyle), this.contents);
}
}
@FunctionalInterface
@OnlyIn(Dist.CLIENT)
public interface LinePosConsumer {
void accept(final Style style, int start, int end);
}
@OnlyIn(Dist.CLIENT)
private class WidthLimitedCharSink implements FormattedCharSink {
private float maxWidth;
private int position;
public WidthLimitedCharSink(float maxWidth) {
Objects.requireNonNull(StringSplitter.this);
super();
this.maxWidth = maxWidth;
}
@Override
public boolean accept(int position, Style style, int codepoint) {
this.maxWidth = this.maxWidth - StringSplitter.this.widthProvider.getWidth(codepoint, style);
if (this.maxWidth >= 0.0F) {
this.position = position + Character.charCount(codepoint);
return true;
} else {
return false;
}
}
public int getPosition() {
return this.position;
}
public void resetPosition() {
this.position = 0;
}
}
@FunctionalInterface
@OnlyIn(Dist.CLIENT)
public interface WidthProvider {
float getWidth(int codepoint, Style style);
}
}引用的其他类
-
- 引用位置:
构造调用 - 关联成员:
ComponentCollector()
- 引用位置:
-
- 引用位置:
参数/方法调用/返回值 - 关联成员:
FormattedText.of()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
StringDecomposer.iterate(), StringDecomposer.iterateBackwards(), StringDecomposer.iterateFormatted()
- 引用位置: