remove TextFlower

This commit is contained in:
Timi
2026-06-17 01:13:58 +08:00
parent 867c5f6602
commit b55a66d6df

View File

@@ -1,334 +0,0 @@
package com.imyeyu.fx.ui.components;
import com.imyeyu.fx.icon.TimiFXIcon;
import com.imyeyu.fx.ui.TimiFXUI;
import com.imyeyu.java.TimiJava;
import javafx.scene.Node;
import javafx.scene.paint.Paint;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 文本流组件,支持插入超链文本,解析富文本:{@link #matcher(String)}
*
* @author 夜雨
* @since 2022-09-05 10:41
*/
public class TextFlower extends TextFlow implements TimiFXUI {
/** 默认构造器 */
public TextFlower() {
getStyleClass().add(CSS.MINECRAFT);
}
/**
* 文本开始,添加一个制表符
*
* @return 本实例
*/
public TextFlower textStart() {
getChildren().add(new Text("\t"));
return this;
}
/**
* 追加文本,左侧补充一个制表符,通常是段落开始
*
* @param text 文本
* @return 本实例
*/
public TextFlower textStart(String text) {
getChildren().add(new Text("\t" + text));
return this;
}
/**
* 追加文本
*
* @param text 文本
* @return 本实例
*/
public TextFlower text(String text) {
getChildren().add(new Text(text));
return this;
}
/**
* 追加文本,左侧补充空格
*
* @param text 文本
* @return 本实例
*/
public TextFlower textLSP(String text) {
getChildren().add(new Text(" " + text));
return this;
}
/**
* 追加文本,右侧补充空格
*
* @param text 文本
* @return 本实例
*/
public TextFlower textRSP(String text) {
getChildren().add(new Text(text + " "));
return this;
}
/**
* 追加链接文本
*
* @param text 显示文本
* @param link 访问链接
* @return 本实例
*/
public TextFlower link(String text, String link) {
getChildren().add(new XHyperlink(text, link));
return this;
}
/**
* 追加链接文本
*
* @param icon 图标
* @param text 显示文本
* @param link 访问链接
* @return 本实例
*/
public TextFlower link(Node icon, String text, String link) {
getChildren().add(new XHyperlink(icon, text, link));
return this;
}
/**
* 追加链接,显示文本为链接
*
* @param value 内容
* @return 本实例
*/
public TextFlower syncLink(String value) {
getChildren().add(new XHyperlink(value, value));
return this;
}
/**
* 设置最后一个文本节点为链接
*
* @param link 链接
* @return 本实例
*/
public TextFlower asLink(String link) {
Node text = getChildren().removeLast();
if (text instanceof Text t) {
getChildren().add(new XHyperlink(t.getText(), link));
}
return this;
}
/**
* 富文本匹配解析字符串,转义使用 '\'
* <br>
* <p>格式标准:
* <ul>
* <li>超链:[可选文本,连接]</li>
* <li>图标:&lt;可选颜色, timi-fx-icon 图标名称&gt;</li>
* <li>重点:`[可选样式,内容]`</li>
* </ul>
*
* @param value 字符串
* @return 本实例
*/
public TextFlower matcher(String value) {
if (TimiJava.isNotEmpty(value)) {
getChildren().addAll(RichMatcher.parse(value));
}
return this;
}
/**
* 富文本匹配
*
* @author 夜雨
* @since 2022-10-10 11:23
*/
private static class RichMatcher {
/**
* 匹配正则
*
* @author 夜雨
* @since 2022-10-10 11:58
*/
private enum Regex {
LINK("\\[(.*?)]"),
ICON("<(.*?)>"),
SPAN("`(.*?)`");
final Pattern pattern;
Regex(String regex) {
this.pattern = Pattern.compile(regex);
}
}
/**
* 重点内容样式
*
* @author 夜雨
* @since 2022-10-10 11:58
*/
private enum Style {
UNDERLINE("u", "underline");
private final String[] matches;
Style(String... matches) {
this.matches = matches;
}
static Style fromMatcher(String matcher) {
Style[] values = values();
for (Style value : values) {
String[] matches = value.matches;
for (String match : matches) {
if (match.equalsIgnoreCase(matcher)) {
return value;
}
}
}
return null;
}
}
/**
* 解析富文本匹配
*
* @param data 文本内容
* @return 匹配节点列表
*/
static List<Node> parse(String data) {
List<Node> result = new ArrayList<>();
Regex[] regexes = Regex.values();
Map<String, Node> nodeMap = new HashMap<>();
int[] insertI = {0};
String value = data;
Matcher matcher;
for (int i = 0; i < regexes.length; i++) {
final int j = i;
matcher = regexes[i].pattern.matcher(value);
value = matcher.replaceAll(matchResult -> {
if (matchResult.start() - 1 != -1) {
if (data.charAt(matchResult.start() - 1) == '\\') {
return matchResult.group();
}
}
nodeMap.put(String.valueOf(insertI[0]), node(regexes[j], value(regexes[j], matchResult.group())));
return "[" + insertI[0]++ + "]";
});
}
// 二次解析
matcher = Pattern.compile("\\[(.*?)]").matcher(value);
int end = 0;
while (matcher.find()) {
if (matcher.start() - 1 != -1) {
if (data.charAt(matcher.start() - 1) == '\\') {
continue;
}
}
if (end != matcher.start()) {
result.add(new Text(value.substring(end, matcher.start())));
}
result.add(nodeMap.get(value(Regex.LINK, matcher.group())));
end = matcher.end();
}
result.add(new Text(value.substring(end)));
return result;
}
/**
* 解析正则匹配值
*
* @param regex 匹配正则
* @param matcherResult 匹配结果
* @return 匹配值
*/
private static String value(Regex regex, String matcherResult) {
return switch (regex) {
case LINK, ICON, SPAN -> matcherResult.substring(1, matcherResult.length() - 1);
};
}
/**
* 解析节点
*
* @param regex 匹配正则
* @param value 匹配值
* @return 节点
*/
private static Node node(Regex regex, String value) {
return switch (regex) {
// 超链
case LINK -> {
int sp = value.indexOf(",");
if (sp == -1) {
yield new XHyperlink(value.trim());
} else {
while (value.charAt(sp - 1) == '\\') {
sp = value.indexOf(",", sp + 1);
}
yield new XHyperlink(value.substring(0, sp).trim(), value.substring(sp + 1).trim());
}
}
// 图标
case ICON -> {
Text icon;
int sp = value.lastIndexOf(",");
if (sp == -1) {
icon = TimiFXIcon.fromName(value);
} else {
icon = TimiFXIcon.fromName(value.substring(sp + 1).trim());
icon.setFill(Paint.valueOf(value.substring(0, sp).trim()));
}
yield icon;
}
// 重点内容
case SPAN -> {
int sp = value.indexOf(",");
if (sp == -1) {
yield new Text(value);
} else {
Text text = new Text(value.substring(sp + 1).trim());
String[] styles = value.substring(0, sp).trim().split(" ");
for (String s : styles) {
Style style = Style.fromMatcher(s);
if (style == null) {
text.setFill(Paint.valueOf(s));
} else {
if (style == Style.UNDERLINE) {
text.setUnderline(true);
}
}
}
yield text;
}
}
};
}
}
}