remove TextFlower
This commit is contained in:
@@ -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>图标:<可选颜色, timi-fx-icon 图标名称></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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user