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

    • 引用位置: 构造调用
    • 关联成员: ComponentCollector()
  • FormattedText

    • 引用位置: 参数/方法调用/返回值
    • 关联成员: FormattedText.of()
  • Style

    • 引用位置: 参数
  • FormattedCharSequence

    • 引用位置: 参数
  • StringDecomposer

    • 引用位置: 方法调用
    • 关联成员: StringDecomposer.iterate(), StringDecomposer.iterateBackwards(), StringDecomposer.iterateFormatted()