package org.jcnc.jnotepad.ui; import javafx.beans.property.StringProperty; import javafx.scene.control.TextArea; import javafx.scene.layout.BorderPane; /** * @author 许轲 */ public class LineNumberTextArea extends BorderPane { private final TextArea mainTextArea; private final TextArea lineNumberArea; static final int[] SIZE_TABLE = {9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, Integer.MAX_VALUE}; private static final int MIN_LINE_NUMBER_WIDTH = 30; public LineNumberTextArea() { mainTextArea = new TextArea(); lineNumberArea = new TextArea(); lineNumberArea.setEditable(false); lineNumberArea.setPrefWidth(MIN_LINE_NUMBER_WIDTH); lineNumberArea.setMinWidth(MIN_LINE_NUMBER_WIDTH); // 设定自定义样式 lineNumberArea.getStyleClass().add("text-line-number"); mainTextArea.getStyleClass().add("main-text-area"); lineNumberArea.textProperty().addListener((observable, oldValue, newValue) -> updateLineNumberWidth()); mainTextArea.textProperty().addListener((observable, oldValue, newValue) -> updateLineNumberArea()); // 当主要文本区域的垂直滚动位置发生变化时,使行号文本区域的滚动位置保持一致 mainTextArea.scrollTopProperty().addListener((observable, oldValue, newValue) -> lineNumberArea.setScrollTop(mainTextArea.getScrollTop())); // 当行号文本区域的垂直滚动位置发生变化时,使主要文本区域的滚动位置保持一致 lineNumberArea.scrollTopProperty().addListener((observable, oldValue, newValue) -> mainTextArea.setScrollTop(lineNumberArea.getScrollTop())); setCenter(mainTextArea); setLeft(lineNumberArea); } private void updateLineNumberWidth() { int numOfLines = mainTextArea.getParagraphs().size(); int count = 1; for (int i = 0; i < SIZE_TABLE.length; i++) { if (numOfLines <= SIZE_TABLE[i]) { count = i + 1; break; } } // 单数字宽度10像素,4为padding=左3+右1 int actualWidth = Math.max(count * 10 + 11, MIN_LINE_NUMBER_WIDTH); if (actualWidth != lineNumberArea.getWidth()) { lineNumberArea.setPrefWidth(actualWidth); } } public StringProperty textProperty() { return mainTextArea.textProperty(); } private void updateLineNumberArea() { // 保存当前的滚动位置 /* 更新行号文本区域的内容,根据主要文本区域的段落数生成行号。 */ double mainTextAreaScrollTop = mainTextArea.getScrollTop(); double lineNumberAreaScrollTop = lineNumberArea.getScrollTop(); int numOfLines = mainTextArea.getParagraphs().size(); StringBuilder lineNumberText = new StringBuilder(); for (int i = 1; i <= numOfLines; i++) { lineNumberText.append(i).append("\n"); } lineNumberArea.setText(lineNumberText.toString()); // 恢复之前的滚动位置 mainTextArea.setScrollTop(mainTextAreaScrollTop); lineNumberArea.setScrollTop(lineNumberAreaScrollTop - 8); } public TextArea getMainTextArea() { return mainTextArea; } }