The following code is supposed to select a word in a JavaFX TextField:
public class NewFXMain extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
final TextInputControl textField = new TextField("Hello World, World!");
Button button = new Button("select");
button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
textField.positionCaret(0);
textField.selectNextWord();
System.out.println(textField.getSelectedText());
}
});
VBox root = new VBox();
root.getChildren().add(textField);
root.getChildren().add(button);
primaryStage.setScene(new Scene(root, 300, 100));
primaryStage.show();
}
}
It prints Hello in the console, however in the interface nothing is selected (highlighted).
If one does the same with a TextArea, the text is correctly selected.
The (wrong) result with a TextField:
And the (correct) result with a TextArea:
What's going on?!?
TextField doesn't show selection unless it has focus (although I'm not sure is it a bug or a feature). You can see selection by using next code:
button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
textField.requestFocus(); // get focus first
textField.positionCaret(0);
textField.selectNextWord();
System.out.println(textField.getSelectedText());
}
});
The answer to the question is that our understanding of native controls in this regard is that they both clear selection when the text field loses focus and that they (typically) select all text when gaining focus. Our intent with UI controls in JavaFX was to have a native feel but a customized look. Of course, we can have our mind changed on how this works, but it has to be balanced with the goal of maintaining a native feel (which includes select-all on focus gained and not showing selection on focus lost, even if selection exists. But since there is little point to there being any selection on focus lost if it is just going to select-all on focus gained and since weird edge cases will still exist as a result, we have to wonder whether it is worth changing this).
Related
I have a javaFX application which takes input from a barcode scanner. The barcode scanner is recognised as a keyboard and always follows its input with a newline after any barcode is scanned (so onAction event is triggered).
At the moment I have a textfield which is always focused and linked to the barcode scanner, however I would like this to be hidden from the user. I tried using setVisible(False) but this seems to disable the textfield (onAction isn't triggered and textfield always empty)
I also tried putting the textfield off the screen which did work however because the screen has to be resizable I now have to use gridPane as the root so its not possible to do this anymore.
Just wondering if there is anyway to make the textfield invisible to the user but still allow it to be enabled?
You have a few possibilities to hide the text field using css. The easiest is to set the opacity to 0
TextField.css
.hidden{
-fx-opacity: 0;
}
Java
GridPane root = new GridPane();
// Add stylesheet
root.getStylesheets().add("TextField.css");
TextField textField = new TextField();
// Add class
textField.getStyleClass().add("hidden");
On the other hand I would really ask myself if this "hack" is the way to go. If your barcode scanner is simply inserting text, maybe the better solution is to add an event handler and handle the keystrokes accordingly:
EventHandler
root.addEventHandler(KeyEvent.ANY,(event)->{
System.out.println(event);
});
You do not actually need to have a TextField in the scene. In fact I wouldn't recommend doing that, since this would interfere with the focus and possibly the layout.
Instead I'd add a listener to the scene (or the root node of the scene making sure it or a descendant can get focus by setting the focusTraversable property to true, if necessary). You may need to use a event filter, if a node that consumes the KEY_PRESSED may have focus:
#Override
public void start(Stage primaryStage) throws IOException {
StackPane root = new StackPane(new Button());
Scene scene = new Scene(root);
final StringBuilder string = new StringBuilder();
scene.addEventFilter(KeyEvent.KEY_PRESSED, evt -> {
KeyCode code = evt.getCode();
if (code.isDigitKey()) {
// input part of the information
String name = code.toString();
string.append(name.charAt(name.length() - 1)); // make sure numpad keys do not
} else if (code == KeyCode.ENTER) {
// submit input
submitCode(string.toString());
string.setLength(0);
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
private void submitCode(String code) {
System.out.println(code);
}
Try using TextField.setText(java.lang.String).
My problem is that my JavaFX window expands further than it should at the bottom, thus showing a weird blank background...
I set stage.setResizable(false) but it doesn't seem to work. I also set
stage.setMinWidth(1280);
stage.setMaxWidth(1280);
stage.setMinHeight(800);
stage.setMaxHeight(800);
I think it's due to some ImageView bigger than the window, but I really would like my window to stick to the max I set and be absolutely not resizable ever.
Here's what it looks like : click here
I also noticed it happens only when I set stage.initStyle(StageStyle.TRANSPARENT).
But I don't want my window to be decorated with stage.initStyle(StageStyle.DECORATED)... :(
Thank you in advance for your help. :)
Have you tried directly using setWidth and setHeight:
stage.setWidth(1280);
stage.setHeight(800);
I think the setMaxWidth, setMinHeight... etc. only works on a decorated screen because its main purpose is to limit the sizes the user can set the window. Here's my example:
public class FixedWindowSize extends Application
{
public static void main(String[] args)
{
Application.launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception
{
Pane p = new Pane();
p.setBackground(new Background(new BackgroundFill(Color.RED, null, null)));
p.setMinSize(6000, 6000); //Make this go out beyond window bounds.
Scene s = new Scene(p);
primaryStage.setScene(s);
primaryStage.initStyle(StageStyle.TRANSPARENT);
//This doesn't work
//primaryStage.setMinWidth(1280);
//primaryStage.setMaxWidth(1280);
//primaryStage.setMinHeight(800);
//primaryStage.setMaxHeight(800);
//This should work.
primaryStage.setWidth(1280);
primaryStage.setHeight(800);
primaryStage.show();
}
}
I am writing a javafx program, for moving text. The program display the background (some red points) and text at the beginning.
Now, I would like to add a menu to select the content of text to display. In addition, I want the scene to display nothing at the beginning, then after I select content of text, the program starts to display everything...
Is there any special command to implement this?
PS: I add the contents (text and shapes) -> "Group" -> "root".
I think what you want is something along this:
public class PersonOverviewController implements Initializable{
#FXML
BorderPane paneWithControls;
#FXML
MenuItem menuItem;
#FXML
Label text;
#FXML
private void initialize() {
paneWithControls.setVisible(false); //hide content
menuItem.setOnAction(new EventHandler<ActionEvent>() { //implementing action listener
#Override
public void handle(ActionEvent event) {
text.setText("Some text you want to display"); //set value for controls
paneWithControls.setVisible(true); //display content
}
});
}
}
EDIT:
If you really want to "hide" the scene you can do something like this:
primaryStage.setScene(null);
and later on:
primaryStage.setScene(sceneObject1);
However this is not good way to achieve result you are looking for. Once you have the scene, just change the root of it, or set properties of it's controls. I have mistaken this also when I was learning JavaFX.
How to resize Swing control which is inside SwingNode in JavaFX8?
Sometimes, I has controls resized inside SwingNode. But SwingNode seems to resist this.
It is said in resize() apidoc, that
Applications should not invoke this method directly. If an application
needs to directly set the size of the SwingNode, it should set the
Swing component's minimum/preferred/maximum size constraints which
will be propagated correspondingly to the SwingNode and it's parent
will honor those settings during layout.
But apparently it does not work.
Example code is below.
The question is: how to allow control to turn bigger?
public class Try_Sizes_01 extends Application {
private static final Logger log = LoggerFactory.getLogger(Try_Sizes_01.class);
private static final String text = "Very Long Text For Appear On Button ";
private static int position = 7;
//private JButton button = new JButton("short");
private JButton button = new JButton(text.substring(0, position));
private SwingNode swingNode = new SwingNode();
{
swingNode.setContent(button);
}
#Override
public void start(Stage stage) throws Exception {
Group group = new Group();
group.getChildren().add(swingNode);
Scene scene = new Scene(group);
stage.setTitle("Try_Sizes_01");
stage.setScene(scene);
stage.show();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
/*
button.setText(button.getText() + text.charAt(position));
position++;
if( position >= text.length() ) {
position=0;
}
*/
button.setPreferredSize(new Dimension((int)button.getPreferredSize().getWidth()+10, (int)button.getPreferredSize().getHeight()));
button.setMinimumSize(new Dimension((int)button.getPreferredSize().getWidth(), (int)button.getPreferredSize().getHeight()));
//button.revalidate();
//button.setBounds(0, 0, (int)button.getBounds().getWidth()+10, (int)button.getBounds().getHeight());
Platform.runLater(new Runnable() {
#Override
public void run() {
//swingNode.autosize(); // does not work
//swingNode.resize(button.getBounds().getWidth(), button.getBounds().getHeight()); // does not work and cancels button resizing
//swingNode.setContent(button); // works sometimes but imperfect
}
});
log.info("Swing thread");
log.info("Preferred width is now = {}", button.getPreferredSize().getWidth());
log.info("Bounds width is now = {}", button.getBounds().getWidth());
}
}).start();
}
});
}
public static void main(String[] args) {
launch(args);
}
}
After fighting for hours with basically the same issue, I finally figured out what was going on.
Basically, the problem is that the parent of the SwingNode is trying to set its size when layout occurs, based on the size of the parent. So when you resize your button, and then trigger a layout, the parent of the SwingNode sets it back to its default size. This is occurring because SwingNode overrides the isResizable() method to return true, giving permission to its parent objects to resize it.
In order to avoid this, you can:
Create a custom subclass of SwingNode which overrides isResizable() to false,
or:
Call setAutosizeChildren(false) on the Group which contains the SwingNode.
The latter technique will probably need to be used if you are defining your classes in FXML.
Note, by the way, that you can still call resize(width,height) on a SwingNode even if it overrides isResizable() to false.
I'm not sure if its exactly the same case, but seems related in the sense of getting swing components to size properly with the parent containers. In my case I had a SwingNode containing a JFreeChart (ChartPanel), which I simply couldn't get to resize properly when the parent frame (a border pane within a SplitPane) was itself resized. In the end i simply added a listener to the height/width properties:
pane.widthProperty().addListener((w,o,n)->c.resizeChart((int)n.intValue(), (int)pane.getHeight()));
pane.heightProperty().addListener((w,o,n)->c.resizeChart((int)pane.getWidth(), (int)n.intValue()));
Nothing else I tried could emulate this.
Thanks
I found the pane listener helps but due to the proprietary nature of my component it still didn't resize. I was using fxml and attempting to place the SwingNode inside a Pane for layout purposes. I then I noticed a number of the examples used a StackPane rather than just a Pane and suddently have just made this change the code worked. This was inside a AnchorPane which also seemed to ensure the initial display of the component filled all the available space. In summary a StackPane within the AnchorPane with all the Anchors set to 0 ensured the controlled filled all the initial available space and then did all the resizing, when the manual resize listeners where added it all started working.
pane.widthProperty().addListener((w,o,n)->c.resizeChart((int)n.intValue(), (int)pane.getHeight()));
pane.heightProperty().addListener((w,o,n)->c.resizeChart((int)pane.getWidth(), (int)n.intValue()));
I am trying to implement a custom tooltip using the javafx.stage.Popup. The sample demo code is:
public class PopupDemo extends Application {
private Popup tooltip;
private final SepiaTone sepiaTone = new SepiaTone();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("PopupDemo");
Label content = new Label();
content.setStyle("-fx-background-color:#FCFBBD; -fx-padding: 5; -fx-border-color: #BFBD3B");
tooltip = new Popup();
tooltip.getContent().add(content);
VBox vbox = new VBox(10);
for (int i = 0; i < 5; i++) {
final Label lbl = new Label("item " + i);
lbl.setStyle("-fx-border-color:darkgray; -fx-background-color:lightgray");
lbl.setMaxSize(80, 60);
lbl.setMinSize(80, 60);
lbl.setAlignment(Pos.CENTER);
lbl.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(final MouseEvent e) {
lbl.setEffect(sepiaTone);
lbl.setStyle("-fx-cursor: hand");
Label content = (Label) tooltip.getContent().get(0);
content.setText(lbl.getText());
tooltip.show(lbl, e.getScreenX(), e.getScreenY());
}
});
lbl.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
lbl.setEffect(null);
lbl.setStyle("-fx-cursor: default");
tooltip.hide();
}
});
vbox.getChildren().add(lbl);
}
StackPane root = new StackPane();
root.setPadding(new Insets(20));
root.getChildren().add(vbox);
primaryStage.setScene(new Scene(root, 600, 400));
primaryStage.show();
}
}
When I move the mouse over the labels the popup shows up and it is working great. But in some cases the two mouse event handlers OnMouseEntered and OnMouseExited are being called continuously one after another. One can reproduce this by running provided example, maximising a window and hovering labels continuously.
Is there a way to avoid this? I'm using JavaFX 2.0.1. Thanks.
It's a classic problem: you put mouse at a point, node receives MouseEntered — tooltip appears under the mouse and covers the node triggering MouseExited.
To avoid that you can change tooltip.show(lbl, e.getScreenX(), e.getScreenY()) call to
tooltip.show(lbl, e.getScreenX() + 1, e.getScreenY() + 1);
This is not really an answer, so much as pointers to things you might try or investigate further.
You could look at the implementation of Tooltip Skin and Behavior to see how it handles some of these cases.
The easiest way to implement a custom popup is just to use a Tooltip, style it the way you need using css and use the Tooltip's setGraphic method to add any custom Node content you want.
If you prefer to use your own implementation, I think you need to keep track of whether the popup has been displayed or not, so you don't try to show it if it is already showing, etc. You may also need invoke the hiding of the popup by having a mouse exit handler installed on the popup itself. You also might want a click to dismiss function for the popup by implementing a mouse click handler on the popup. You should also consider whether you should do a straight subclass of Popup or PopupControl, though using Popup as you have is likely more straightforward.