gpt4 book ai didi

Why does selectRange() in JavaFX TextArea sometimes not highlight the selection?(为什么JavaFX TextArea中的selectRange()有时不突出显示所选内容?)

转载 作者:bug小助手 更新时间:2023-10-25 11:03:17 30 4
gpt4 key购买 nike



I have written the following code in my JavaFX main class:

我在我的JavaFX主类中编写了以下代码:


package jfxTest;

import java.util.Random;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class Main extends Application {
public static void main(String[] args) {
Application.launch(args);
}

private static int selectionIndex = 0;
private static TextArea textArea;

@Override
public void start(Stage primaryStage) {
System.out.println("Starting JavaFX Window...");
StackPane rootPane = new StackPane();
textArea = new TextArea();

textArea.setText("TEST");
textArea.setEditable(false);
createRandomOptions(8);

textArea.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode().equals(KeyCode.UP)) {
select(selectionIndex <= 0 ? 0 : selectionIndex - 1);
}
if (event.getCode().equals(KeyCode.DOWN)) {
String[] lines = textArea.getText().split("\n");
select(selectionIndex >= lines.length - 1 ? lines.length - 1 : selectionIndex + 1);
}
});

rootPane.getChildren().add(textArea);
Scene scene = new Scene(rootPane, 900, 500);
primaryStage.setScene(scene);
primaryStage.show();
System.out.println("Created window.");
System.out.println("\nNow press up and down keys to navigate:\n"
+ "Notice, that although a new selection is displayed in the console, it is not\n"
+ "highlighted in the window itself. Even a call to getSelectedText() works fine.\n");
}

/**
* Creates a bunch of random options in textArea.
* @param newOptionCount
*/
private static void createRandomOptions(int newOptionCount) {
for (int i = 0; i < newOptionCount; i++) {
textArea.appendText("\nSEL" + (new Random().nextInt(10000)));
}
}

private static void select(int newSelectionIndex) {
String[] lines = textArea.getText().split("\n");

System.out.println("New selection index: " + newSelectionIndex);
selectionIndex = newSelectionIndex;

// Determine selection indexes
int selectionStart = 0;
int selectionEnd = 0;
for (int i = 0; i < newSelectionIndex; i++) {
selectionStart += lines[i].length() + 1;
}
selectionEnd = selectionStart + lines[newSelectionIndex].length();

// Does not work. Selection does need to be applied twice by the user to be actually highlighted.
textArea.selectRange(selectionStart, selectionEnd);

System.out.println("Selected text: " + textArea.getSelectedText() + "\n");
}
}


I know it looks like a mess and is not exactly clean, but you should get the point.
Now the problem is as follows:
When I navigate in the TextArea with the arrow keys (specifically UP and DOWN), the selection only becomes visible, when it is applied twice by the user(only at the very top and bottom), although, a call to selectRange() is made everytime.

我知道它看起来很乱,也不是很干净,但你应该明白这一点。现在的问题是:当我使用箭头键(特别是向上和向下)在TextArea中导航时,只有当用户应用两次选择(仅在最上面和最下面)时,选择才变得可见,尽管每次都会调用seltRange()。


Why is this? Is it a bug in the JavaFX library, or am I missing something?

这是为什么呢?这是JavaFX库中的一个bug,还是我错过了什么?


I have searched the problem a few times already, but I've found no results yet.
I haven't tested this using AWT Swing yet, but I am quite confident, that it would work fine using it.

我已经搜索了这个问题几次,但还没有找到任何结果。我还没有使用AWT Swing测试这一点,但我非常有信心,使用它会工作得很好。


My Java is OpenJDK 19 with JavaFX version 19.

我的Java是带有JavaFX版本19的OpenJDK 19。


更多回答

@James_D ListView may be a good option I haven't thought about. Could you give me a simple implementation with a few options to select from so I can get an idea of how it works?

@James_D ListView可能是一个我还没有考虑过的好选择。你能给我一个简单的实现,有几个选项可供选择,这样我就可以了解它的工作原理了吗?

Added example (both of using a ListView and of doing this with a TextArea as an answer.

添加了示例(使用ListView和使用TextArea作为答案来执行此操作。

优秀答案推荐

It is never a particularly good idea to add low-level event handling (such as mouse and keyboard event handlers) to high-level UI controls. You always stand the risk of interfering with the built-in functionality of the controls, or of the built-in functionality interfering with your event handlers resulting in not giving you the functionality you want.

将低级事件处理(如鼠标和键盘事件处理程序)添加到高级UI控件从来都不是一个特别好的主意。您总是面临干扰控件的内置功能的风险,或者内置功能干扰事件处理程序导致无法提供您想要的功能的风险。


In your case, the latter is happening: the TextArea already implements changing the selection when keys are pressed. If the arrow keys are pressed with no modifier key, then the selection will be cleared. This default behavior is occurring after your key press is invoked, so the default behavior is the behavior you see.

在您的例子中,后一种情况正在发生:TextArea已经实现了在按下键时更改选择。如果在没有修改键的情况下按下箭头键,则选择将被清除。此默认行为是在您的按键被调用之后发生的,因此默认行为是您看到的行为。


You can see what is happening by adding

您可以通过添加以下内容查看正在发生的情况


textArea.selectionProperty().addListener((obs, oldSel, newSel) -> 
System.out.printf("Selection change: %s -> %s%n", oldSel, newSel)
);

Note you can workaround this by adding event.consume() to both the if() { ... } blocks in your key event handler, but I don't think this is a particularly good solution: the solutions below are better imho.

注您可以通过向键事件处理程序中的两个if(){...}块添加event.Consumer()来解决此问题,但我不认为这是一个特别好的解决方案:下面的解决方案更好。


It looks like you are simply using the wrong control here. If you have a non-editable TextArea in which you are regarding each line as a distinct item, then it sounds like you are trying to re-implement a ListView, which already implements things like selection of the individual items. You can reimplement your example simple with:

看起来您只是在这里使用了错误的控件。如果您有一个不可编辑的TextArea,您将其中的每一行视为一个不同的项,那么听起来您似乎是在尝试重新实现一个ListView,它已经实现了选择各个项之类的事情。您可以使用以下命令重新实现您的示例:


import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import java.util.Random;

public class Main extends Application {
public static void main(String[] args) {
Application.launch(args);
}

private ListView<String> listView;

@Override
public void start(Stage primaryStage) {
System.out.println("Starting JavaFX Window...");
StackPane rootPane = new StackPane();
listView = new ListView<>();


listView.getItems().add("TEST");
createRandomOptions(8);


rootPane.getChildren().add(listView);
Scene scene = new Scene(rootPane, 900, 500);
primaryStage.setScene(scene);
primaryStage.show();
System.out.println("Created window.");
System.out.println("\nNow press up and down keys to navigate:\n"
+ "Notice, that although a new selection is displayed in the console, it is not\n"
+ "highlighted in the window itself. Even a call to getSelectedText() works fine.\n");
}


/**
* Creates a bunch of random options in textArea.
* @param newOptionCount
*/
private void createRandomOptions(int newOptionCount) {
for (int i = 0; i < newOptionCount; i++) {
listView.getItems().add("SEL" + (new Random().nextInt(10000)));
}
}

}

If you really want to modify what is selected in a text area, use a TextFormatter with a filter that modifies the selection. This would looks something like this (though, again, I think this is just reinventing the wheel). Note this also behaves the way you (presumably) want on mouse clicks:

如果确实要修改文本区域中所选的内容,请使用带有可修改所选内容的滤镜的TextForMatter。这看起来会像这样(不过,我再次认为这只是重新发明轮子)。注意:这也是您(可能)希望鼠标点击时的行为方式:


import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.IndexRange;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import java.util.Random;

public class Main extends Application {
public static void main(String[] args) {
Application.launch(args);
}

private TextArea textArea;

@Override
public void start(Stage primaryStage) {
System.out.println("Starting JavaFX Window...");
StackPane rootPane = new StackPane();
textArea = new TextArea();

textArea.selectionProperty().addListener((obs, oldSel, newSel) -> System.out.printf("Selection change: %s -> %s%n", oldSel, newSel));

textArea.setText("TEST");
textArea.setEditable(false);
createRandomOptions(8);

textArea.setTextFormatter(new TextFormatter<String>(this::modifySelection));

rootPane.getChildren().add(textArea);
Scene scene = new Scene(rootPane, 900, 500);
primaryStage.setScene(scene);
primaryStage.show();
System.out.println("Created window.");
System.out.println("\nNow press up and down keys to navigate:\n"
+ "Notice, that although a new selection is displayed in the console, it is not\n"
+ "highlighted in the window itself. Even a call to getSelectedText() works fine.\n");
}

private TextFormatter.Change modifySelection(TextFormatter.Change change) {
IndexRange selection = change.getSelection();
String[] lines = change.getControlNewText().split("\n");
int lineStart = 0 ;
for (String line : lines) {
int lineEnd = lineStart + line.length();
if (lineStart <= selection.getStart() && lineEnd >= selection.getStart()) {
change.setAnchor(lineStart);
}
if (lineStart <= selection.getEnd() && lineEnd >= selection.getEnd()) {
change.setCaretPosition(lineEnd);
}
lineStart += line.length() + 1; // +1 to account for line terminator itself
}
return change;
}

/**
* Creates a bunch of random options in textArea.
* @param newOptionCount
*/
private void createRandomOptions(int newOptionCount) {
for (int i = 0; i < newOptionCount; i++) {
textArea.appendText("\nSEL" + (new Random().nextInt(10000)));
}
}

}

更多回答

Thank you very much! Both versions work flawlessly and without any problems. I'll go with the ListView, since this is a direct implementatuon of what I wanted. Explanation is very detailed as well.

非常感谢!这两个版本都运行得天衣无缝,没有任何问题。我将使用ListView,因为这是我想要的东西的直接实现。解释也非常详细。

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com