package com.imyeyu.fx.ui.components; import com.imyeyu.fx.ui.TimiFXUI; import com.imyeyu.java.ref.Ref; import com.sun.javafx.scene.control.skin.Utils; import javafx.application.Platform; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.ListChangeListener; import javafx.scene.Cursor; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.control.ScrollPane; import javafx.scene.control.Skin; import javafx.scene.control.TextArea; import javafx.scene.control.skin.TextAreaSkin; import javafx.scene.layout.Region; import javafx.scene.paint.Paint; import javafx.scene.text.Text; import javafx.scene.text.TextAlignment; import java.util.List; /** * 可选中的标签组件,实际上是无样式文本域,此组件适应布局最大宽度 * * @author 夜雨 * @since 2022-01-29 01:24 */ public class SelectableLabel extends TextArea implements TimiFXUI, TimiFXUI.Colorful { private static final String STYLE_CLASS = "selectable-label"; private final ObjectProperty textFillProperty; private final ObjectProperty textAlignmentProperty; /** 默认构造 */ public SelectableLabel() { this(""); } /** * 构造器 * * @param text 文本内容 */ public SelectableLabel(String text) { super(text); textFillProperty = new SimpleObjectProperty<>(BLACK); textAlignmentProperty = new SimpleObjectProperty<>(TextAlignment.LEFT); setCursor(Cursor.TEXT); setEditable(false); setWrapText(true); setMaxWidth(Double.MAX_VALUE); setMinSize(Double.MIN_VALUE, Double.MIN_VALUE); setPrefHeight(0); setPrefSize(0, 0); getStyleClass().setAll(STYLE_CLASS, CSS.MINECRAFT); focusedProperty().addListener((obs, o, isFocused) -> { if (!isFocused) { deselect(); } }); } @Override protected Skin createDefaultSkin() { Skin defaultSkin = super.createDefaultSkin(); if (defaultSkin instanceof TextAreaSkin skin) { try { ScrollPane sp = Ref.getFieldValue(skin, "scrollPane", ScrollPane.class); sp.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); if (sp.getContent() instanceof Region region) { region.heightProperty().addListener((obs, o, newHeight) -> Platform.runLater(() -> { // 需要 runLater,因为是 Region 适应 Skin 变化 setPrefHeight(newHeight.doubleValue()); })); } Group paragraphNodes = Ref.getFieldValue(skin, "paragraphNodes", Group.class); paragraphNodes.getChildren().addListener((ListChangeListener) c -> { if (c.next()) { if (c.wasAdded()) { bindTextStyle(c.getAddedSubList()); } } }); bindTextStyle(paragraphNodes.getChildren()); widthProperty().addListener((obs, oldValue, newValue) -> { if (oldValue.doubleValue() < newValue.doubleValue()) { if (paragraphNodes.getChildren().getFirst() instanceof Text text) { setPrefHeight(Utils.computeTextHeight(getFont(), getText(), getWidth(), text.getBoundsType())); } } }); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } return defaultSkin; } /** * 绑定文本样式 * * @param list 文本节点,必须是 {@link Text} */ private void bindTextStyle(List list) { for (Node node : list) { if (node instanceof Text text) { text.fillProperty().bind(textFillProperty); text.textAlignmentProperty().bind(textAlignmentProperty); } } } /** * 设置字体颜色 * * @param textFill 字体颜色 */ public void setTextFill(Paint textFill) { this.textFillProperty.set(textFill); } /** * 获取字体颜色 * * @return 字体颜色 */ public Paint getTextFill() { return textFillProperty.get(); } /** * 获取字体颜色绑定 * * @return 字体颜色绑定 */ public ObjectProperty textFillProperty() { return textFillProperty; } /** * 设置文本对齐方式 * * @param textAlignment 文本对齐方式 */ public void setTextAlignment(TextAlignment textAlignment) { this.textAlignmentProperty.set(textAlignment); } /** * 获取文本对齐方式 * * @return 文本对齐方式 */ public TextAlignment getTextAlignment() { return textAlignmentProperty.get(); } /** * 获取文本对齐方式绑定 * * @return 文本对齐方式绑定 */ public ObjectProperty textAlignmentProperty() { return textAlignmentProperty; } }