I want to be able to read my sliders value, and therefore I use an event listener for my controller class:
//Controller class
#FXML
private Slider gridSlider;
#FXML
void sliderChange(MouseDragEvent event) {
int sliderValue = (int) gridSlider.getValue();
System.out.println(sliderValue);
}
//My FXML class
<Slider fx:id="gridSlider" blockIncrement="1.0" layoutX="203.0" layoutY="84.0" majorTickUnit="8.0" max="32.0" min="8.0" minorTickCount="7" nodeOrientation="LEFT_TO_RIGHT" onMouseDragReleased="#sliderChange" prefHeight="38.0" prefWidth="180.0" showTickLabels="true" showTickMarks="true" snapToTicks="true" value="8.0">
I have tried every single MouseDragEvent and none of the MouseDragEvents has actually been called (like on drag detected which should have been called the moment i start dragging the slider). I found this post, but it didn't solve my problem.
JavaFX mouse drag events not firing
The MouseDragEvent is associated with "full press-drag-release" gestures. Such gestures have to be explicitly started by calling Node#startFullDrag() inside a DRAG_DETECTED handler. The documentation of MouseEvent provides more information about the different dragging gestures.
With that being said, you probably don't want to use mouse events to know when your Slider's value has changed. It would be listen to one or more properties of your Slider. For instance, you can listen or bind to the value property to always know the current value. There's also the valueChanging property:
When true, indicates the current value of this Slider is changing. It provides notification that the value is changing. Once the value is computed, it is reset back to false.
That property will be true while the user is dragging the thumb and will become false once the user "drops" the thumb. To listen to the property while using FXML you can inject the Slider into the controller and add a listener to it in the initialize method:
public class Controller {
#FXML private Slider slider;
#FXML
private void initialize() {
slider.valueChangingProperty().addListener((obs, ov, nv) -> {
if (!nv) {
// do something...
}
});
}
}
If you want, however, you can add such a listener via the FXML file—see Introduction to FXML:
<StackPane ...>
<Slider onValueChangingChange="#handleValueChangingChange"/>
</StackPane>
public class Controller {
#FXML
private void handleValueChangingChange(ObservableValue<Boolean> obs, Boolean ov, Boolean nv) {
if (!nv) {
// Do something...
}
}
}
Note: My examples use the valueChanging property because that would seem to provide the behavior you want, as you were trying to listen to when the mouse was released. However, the value can be changed programmatically which, as far as I know, does not affect the valueChanging property. Thus, if you want to always know the current value, you need to observe the value property.
Related
I have a parent controller say HomeController
It has a node SidePanel (JFXDrawer) with SidePanelController and a node anchorPane with varying controller.
HomeController
|
/ \
/ \
/ \
anchorPane SidePanel
(Controller (Controller = SidePanelController)
= varies)
The anchorPane node should load multiple fxml views with menu buttons clicked from SidePanelController.
The problem here is in SidePanelController since the buttons are inside it, I cannot directly load onanchorPane as for SidePanelController the node anchorPane does not exists
This question seems duplicate of this but its not because the parent controller is waiting for scene to close so it fetches back the data to parent controller. But in my case, every time I click on menu button, it will load a view accordingly.
Can anybody provide resources for making controller for JFXDrawer (as child node).
If say, I have a side navigation drawer sidepanel.fxml like this
And I have a HomeScreen like this
So by using following code, I stacked drawer in my homecontroller
try {
SidePanelController controller = new SidePanelController();
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/sidepanel.fxml"));
loader.setController(controller);
Parent root = loader.load();
drawer.setSidePane(root);
} catch (IOException err) {
err.printStackTrace();
}
Finally, I will be getting this as combined output
Now what I want is whenever I try to click on say Lorry Receipt button on Side Navigation Panel, it should trigger the node residing on Parent controller. Even the event which will pass data back to parent controller without closing the child node will work.
As #Sedrick suggested, I initialized events on all buttons of SidePanelController in HomeController (Parent) itself. At first it returned NPE, but later I let the buttons initialize and then fetch it back to parent controller.
So here is the solution. It might be non-ethical, so other alternatives still appreciated.
public class SidePanelController implements Initializable {
#FXML
private JFXButton btn_lr;
#FXML
private JFXButton btn_shipment;
#FXML
private JFXButton btn_add_inward;
}
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
//Your other activities when sidedrawer/pane initializes
}
//After I initialize, I would like all of these buttons to be fetched to my Parent controller. So instead of me passing each button separately, I made a list to save my time.
public ObservableList<JFXButton> fetchAllButtons(){
ObservableList<JFXButton> allbuttons = FXCollections.observableArrayList(btn_lr, btn_add_inward, btn_shipment);
return allbuttons;
}
Now in HomeController or ParentController, I fetch all these buttons and create events for it separately. So here goes the code.
public class HomeController implements Initializable {
#FXML
private JFXHamburger hamburger;
#FXML
private JFXDrawer drawer;
//Creating Hamburger Task to toggle sidebar
HamburgerBackArrowBasicTransition burgerTask;
//Declaring sidepanelcontroller globally because I need it in multiple methods.
SidePanelController controller = new SidePanelController();
//Finally a list to fetch the list of all buttons from SidePanelController
ObservableList<JFXButton> sidebuttons;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
//Using hamburger to toggle my side drawer
burgerTask = new HamburgerBackArrowBasicTransition(hamburger);
//Initializing the scene of drawer/SidePanel FXML file on Home.fxml or parent fxml itself
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/sidepanel.fxml"));
loader.setController(controller);
Parent root = loader.load();
drawer.setSidePane(root);
sidebuttons = controller.fetchAllButtons();
//Make sure not to declare this before initializing or else it returns NPE
} catch (Exception err) {
err.printStackTrace();
}
//Finally you can declare your tasks for each button by declaring events on each of those buttons. I thought only common but different thing you can between buttons is the name of it. So I used switch statement just to point out each button separately. (I know this might be unethical, but works for me though)
for (JFXButton button: sidebuttons) {
switch (button.getText()){
case "Lorry Receipt":
button.setOnAction(actionEvent -> {
//Your actions here when button with name Lorry Receipt is pressed
});
break;
case "Shipment Memo":
button.setOnAction(actionEvent -> {
//Your actions when button with name shipment memo is pressed
});
break;
case "Inward Challan":
button.setOnAction(actionEvent -> {
//Your actions when button with name Inward Challan is pressed
});
break;
}
}
}
}
Other Advantage I found with this is, I don't have to show ProgressBar/ProgressIndicator of each scene separately. Since Child Component's ActionEvent is on ParentNode, the ProgressBar/Indicator can be binded to it and works like a charm.
I've a custom dialog with several UI elements. Some TextFields are for numeric input. This dialog does not close when the escape key is hit and the focus is on any of the numeric text fields. The dialog closes fine when focus is on other TextFields which do not have this custom TextFormatter.
Here's the simplified code:
package application;
import java.text.DecimalFormat;
import java.text.ParsePosition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
TextField name = new TextField();
HBox hb1 = new HBox();
hb1.getChildren().addAll(new Label("Name: "), name);
TextField id = new TextField();
id.setTextFormatter(getNumberFormatter()); // numbers only
HBox hb2 = new HBox();
hb2.getChildren().addAll(new Label("ID: "), id);
VBox vbox = new VBox();
vbox.getChildren().addAll(hb1, hb2);
Dialog<ButtonType> dialog = new Dialog<>();
dialog.setTitle("Number Escape");
dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
dialog.getDialogPane().setContent(vbox);
Platform.runLater(() -> name.requestFocus());
if (dialog.showAndWait().get() == ButtonType.OK) {
System.out.println("OK: " + name.getText() + id.getText());
} else {
System.out.println("Cancel");
}
} catch (Exception e) {
e.printStackTrace();
}
}
TextFormatter<Number> getNumberFormatter() {
// from https://stackoverflow.com/a/31043122
DecimalFormat format = new DecimalFormat("#");
TextFormatter<Number> tf = new TextFormatter<>(c -> {
if (c.getControlNewText().isEmpty()) {
return c;
}
ParsePosition parsePosition = new ParsePosition(0);
Object object = format.parse(c.getControlNewText(), parsePosition);
if (object == null || parsePosition.getIndex() < c.getControlNewText().length()) {
return null;
} else {
return c;
}
});
return tf;
}
public static void main(String[] args) {
launch(args);
}
}
How do I close the dialog when escape key is hit while focus is on id?
The Problem
Before offering a solution I think it's important, or at least interesting, to understand why having a TextFormatter seems to change the behavior of the Dialog. If this doesn't matter to you, feel free to jump to the end of the answer.
Cancel Buttons
According to the documentation of Button, a cancel button is:
the button that receives a keyboard VK_ESC press, if no other node in the scene consumes it.
The end of that sentence is the important part. The way cancel buttons, as well as default buttons, are implemented is by registering an accelerator with the Scene that the Button belongs to. These accelerators are only invoked if the appropriate KeyEvent bubbles up to the Scene. If the event is consumed before it reaches the Scene, the accelerator is not invoked.
Note: To understand more about event processing in JavaFX, especially terms such as "bubbles" and "consumed", I suggest reading this tutorial.
Dialogs
A Dialog has certain rules regarding how and when it can be closed. These rules are documented here, in the Dialog Closing Rules section. Suffice to say, basically everything depends on which ButtonTypes have been added to the DialogPane. In your example you use one of the predefined types: ButtonType.CANCEL. If you look at the documentation of that field, you'll see:
A pre-defined ButtonType that displays "Cancel" and has a ButtonBar.ButtonData of ButtonBar.ButtonData.CANCEL_CLOSE.
And if you look at the documentation of ButtonData.CANCEL_CLOSE, you'll see:
A tag for the "cancel" or "close" button.
Is cancel button: True
What this means, at least for the default implementation, is that the Button created for said ButtonType.CANCEL will be a cancel button. In other words, the Button will have its cancelButton property set to true. This is what allows one to close a Dialog by pressing the Esc key.
Note: It's the DialogPane#createButton(ButtonType) method that's responsible for creating the appropriate button (and can be overridden for customization). While the return type of that method is Node it is typical, as documented, to return an instance of Button.
The TextFormatter
Every control in (core) JavaFX has three components: the control class, the skin class, and the behavior class. The latter class is responsible for handling user input, such as mouse and key events. In this case, we care about TextInputControlBehavior and TextFieldBehavior; the former is the superclass of the latter.
Note: Unlike the skin classes, which became public API in JavaFX 9, the behavior classes are still private API as of JavaFX 12.0.2. Much of what's described below are implementation details.
The TextInputControlBehavior class registers an EventHandler that reacts to the Esc key being pressed, invoking the cancelEdit(KeyEvent) method of the same class. All the base implementation of this method does is forward the KeyEvent to the TextInputControl's parent, if it has one—resulting in two event dispatching cycles for some unknown (to me) reason. However, the TextFieldBehavior class overrides this method:
#Override
protected void cancelEdit(KeyEvent event) {
TextField textField = getNode();
if (textField.getTextFormatter() != null) {
textField.cancelEdit();
event.consume();
} else {
super.cancelEdit(event);
}
}
As you can see, the presence of a TextFormatter causes the KeyEvent to be unconditionally consumed. This stops the event from reaching the Scene, the cancel button is not fired, and thus the Dialog does not close when the Esc key is pressed while the TextField has the focus. When there is no TextFormatter the super implementation is invoked which, as stated before, simply forwards the event to the parent.
The reason for this behavior is hinted at by the call to TextInputControl#cancelEdit(). That method has a "sister method" in the form of TextInputControl#commitValue(). If you look at the documentation of those two methods, you'll see:
If the field is currently being edited, this call will set text to the last commited value.
And:
Commit the current text and convert it to a value.
Respectively. That doesn't explain much, unfortunately, but if you look at the implementation their purpose becomes clear. A TextFormatter has a value property which is not updated in real time while typing into the TextField. Instead, the value is only updated when it's committed (e.g. by pressing Enter). The reverse is also true; the current text can be reverted to the current value by cancelling the edit (e.g. by pressing Esc).
Note: The conversion between String and an object of arbitrary type is handled by the StringConverter associated with the TextFormatter.
When there's a TextFormatter, the act of cancelling the edit is deemed an event-consuming scenario. This makes sense, I suppose. However, even when there's nothing to cancel the event is still consumed—this doesn't make as much sense to me.
A Solution
One way to fix this is to dig into the internals, using reflection, as is shown in kleopatra's answer. Another option is to add an event filter to the TextField or some ancestor of the TextField that closes the Dialog when the Esc key is pressed.
textField.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode() == KeyCode.ESCAPE) {
event.consume();
dialog.close();
}
});
If you'd like to include the cancel-edit behavior (cancel without closing) then you should only close the Dialog if there's no edit to cancel. Take a look at kleopatra's answer to see how one might determine whether or not a cancel is needed. If there is something to cancel simply don't consume the event and don't close the Dialog. If there isn't anything to cancel then just do the same as the code above (i.e. consume and close).
Is using an event filter the "recommended way"? It's certainly a valid way. JavaFX is event-driven like most, if not all, mainstream UI toolkits. For JavaFX specifically that means reacting to Events or observing Observable[Value]s for invalidations/changes. A framework built "on top of" JavaFX may add its own mechanisms. Since the problem is an event being consumed when we don't want it to be, it is valid to add your own handlers to implement the desired behavior.
The question already has an excellent answer, nothing to add. Just wanted to demonstrate how to tweak the behavior's InputMap to inject/replace our own mappings (as a follow-up to my comment). Beware: it's dirty in reflectively accessing a skin's behavior (private final field) and using internal api (Behavior/InputMap didn't make it into public, yet).
As Slaw pointed out, it's the behavior that prevents the ESCAPE from bubbling up to the cancel button if the TextField has a TextFormatter installed. IMO, it's not misbehaving in that case, just overshooting: the cancel/default buttons should be triggered on ESCAPE/ENTER if and only if no other had used it to change the state of the any input nodes (my somewhat free interpretation of consumed - had done some research on general UX guidelines that I can't find right now, embarassingly ...)
Applied to a form containing both a textField with textFormatter and a cancel button (aka: isCancelButton is true)
if the textField has uncommitted text, a cancel should revert the edit to the most recent committed value and consume the event
if the textField is committed it should let it bubble up to trigger the cancel button
The implementation of cancelEdit in behavior doesn't distinguish between those two states, but always consumes it. The example below implements the expected (by me, at least) behavior. It has
a helper method to decide whether or not is dirty (aka: the textField has an uncommitted edit)
a event handling method that checks for dirtyness, calls cancel and consumes the event only it had been dirty
a configuration method that tweaks the textFields inputMap such that the mapping is replaced by our own.
Note that this is a PoC: doesn't belong into helpers but into a custom skin (at the very least, ideally should be done by the behavior). And it is missing similar support for the ENTER .. which is slightly more involved because it has to take actionHandlers into account (which behavior tries to but fails to achieve)
To test the example:
compile (note: you need to reflectively access a private field, use whatever you have at hand - we all do, don't we) and run
type something into the field
press escape: the field's text is reverted to its initial value
press escape again: the cancel button is triggered
The example code:
public class TextFieldCancelSO extends Application {
/**
* Returns a boolean to indicate whether the given field has uncommitted
* changes.
*
* #param <T> the type of the formatter's value
* #param field the field to analyse
* #return true if the field has a textFormatter with converter and
* uncommitted changes, false otherwise
*/
public static <T> boolean isDirty(TextField field) {
TextFormatter<T> textFormatter = (TextFormatter<T>) field.getTextFormatter();
if (textFormatter == null || textFormatter.getValueConverter() == null) return false;
String fieldText = field.getText();
StringConverter<T> valueConverter = textFormatter.getValueConverter();
String formatterText = valueConverter.toString(textFormatter.getValue());
// todo: handle empty string vs. null value
return !Objects.equals(fieldText, formatterText);
}
/**
* Install a custom keyMapping for ESCAPE in the inputMap of the given field.
* #param field the textField to configure
*/
protected void installCancel(TextField field) {
// Dirty: reflectively access the behavior
// needs --add-exports at compile- and runtime!
// note: FXUtils is a custom helper class not contained in core fx, use your own
// helper or write the field access code as needed.
TextFieldBehavior behavior = (TextFieldBehavior) FXUtils.invokeGetFieldValue(
TextFieldSkin.class, field.getSkin(), "behavior");
// Dirty: internal api/classes
InputMap inputMap = behavior.getInputMap();
KeyBinding binding = new KeyBinding(KeyCode.ESCAPE);
// custom mapping that delegates to helper method
KeyMapping keyMapping = new KeyMapping(binding, e -> {
cancelEdit(field, e);
});
// by default, mappings consume the event - configure not to
keyMapping.setAutoConsume(false);
// remove old
inputMap.getMappings().remove(keyMapping);
// add new
inputMap.getMappings().add(keyMapping);
}
/**
* Custom EventHandler that's mapped to ESCAPE.
*
* #param field the field to handle a cancel for
* #param ev the received keyEvent
*/
protected void cancelEdit(TextField field, KeyEvent ev) {
boolean dirty = isDirty(field);
field.cancelEdit();
if (dirty) {
ev.consume();
}
}
private Parent createContent() {
TextFormatter<String> fieldFormatter = new TextFormatter<>(
TextFormatter.IDENTITY_STRING_CONVERTER, "textField ...");
TextField field = new TextField();
field.setTextFormatter(fieldFormatter);
// listen to skin: behavior is available only after it's set
field.skinProperty().addListener((src, ov, nv) -> {
installCancel(field);
});
// just to see the state of the formatter
Label fieldValue = new Label();
fieldValue.textProperty().bind(fieldFormatter.valueProperty());
// add cancel button
Button cancel = new Button("I'm the cancel");
cancel.setCancelButton(true);
cancel.setOnAction(e -> LOG.info("triggered: " + cancel.getText()));
HBox fields = new HBox(100, field, fieldValue);
BorderPane content = new BorderPane(fields);
content.setBottom(cancel);
return content;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TextFieldCancelSO.class.getName());
}
I'm playing around with JavaFX for the first time on a personal project.
I want to be able to update the content in a Tab (adding a PieChart), but from outside the FXML controller, can anyone tell me if that's possible? How would I get a reference to the relevant tab, and is there a way to specify the location of the item I am adding?
UPDATE: Added some example code.
Hope this gives a clear idea of what I'm trying to do:
The interface:
interface ChartStrategy {
public void DisplayChart(Info info)
}
Two implementations:
public class BarChartStrategy extends ChartStrategy {
public void DisplayChart(Info info)
{
// Create bar charts on specific tabs in the UI
}
}
public class PieChartStrategy extends ChartStrategy {
public void DisplayChart(Info info)
{
// Create pie charts on specific tabs in the UI
}
}
The context:
public class ChartContext {
private ChartStrategy strategy;
public void setChartStrategy(ChartStrategy strategy) {
this.strategy = strategy;
}
public void drawGraphs(Info info) {
strategy.DisplayChart(info);
}
}
In my Controller, I'm reading in a file the user selects from which the data to generate the charts is parsed, .e.g.
#FXML
private void handleButtonAction(ActionEvent event) {
// Load the file and parse the data
...
ChartContext charts = new ChartContext();
charts.setChartStrategy(new PieChartStrategy());
}
So my original thought was that I could draw the charts from the DisplayChart function in the implementations, but it seems that's not a good idea - can anyone give me some advice here on the best way to get this to work?
I would refactor this a bit.
First, letting the strategy display the chart is a bit inflexible. You are giving the strategy two responsibilities: first to decide how to represent the data visually (choose a chart) and second to actually display it somewhere. That violates the single responsibility principle.
So I would do
interface ChartStrategy {
public Chart generateChart(Info info)
}
and similarly for the implementations, of course. Then the responsibility of the strategy is simply to provide a chart: the context can decide what to do with it.
(You can also consider whether returning a Chart here is too rigid: maybe you just want it to return a Parent, or Node. Then your "chart" could be, e.g., a TableView, for example.)
In the theoretical descriptions of the strategy pattern, at least in my interpretation, the "context" just represents "something that is using the strategy". So your context is likely a view or controller (depending on your choice of MVC variant...). As a trivial example you might have something like
public class ChartTab {
private ChartStrategy chartGenerator ;
public void setChartGenerator(ChartStrategy chartGenerator) {
this.chartGenerator = chartGenerator ;
}
public Tab createChartTab(Info info) {
Tab tab = new Tab();
tab.setContent(chartGenerator.generateChart(info));
return tab ;
}
}
and then in your controller
#FXML
private TabPane tabPane ;
#FXML
private void handleButtonAction(ActionEvent event) {
// Load the file and parse the data
...
ChartTab chartTab = new ChartTab();
chartTab.setChartGenerator(new PieChartStrategy());
tabPane.getTabs().add(chartTab.getTab(info));
}
It's also possible just to consider the controller the context (if for a fixed controller you are just creating one kind of chart, which depends on how you split up the FXML files and their corresponding controllers):
public class MyController {
private ChartStrategy chartGenerator ;
#FXML
private TabPane tabPane ;
public MyController(ChartStrategy chartGenerator) {
this.chartGenerator = chartGenerator ;
}
#FXML
private void handleButtonAction(ActionEvent event) {
// Load the file and parse the data
...
Tab tab = new Tab();
tab.setContent(chartGenerator.generateChart(info));
tabPane.getTabs().add(tab);
}
}
Note this controller doesn't have a no-arg constructor, so you cannot use the fx:controller attribute in the FXML file (i.e. remove that attribute from the FXML file). Instead, you'd do
FXMLLoader loader = new FXMLLoader("/path/to/DataDisplay.fxml");
MyController controller = new MyController(new PieChartStrategy());
loader.setController(controller);
Parent root = loader.load();
Now you have an FXML and controller with the functionality to generate charts and display them in a tab pane (or whatever), but the details of what kind of chart is generated are factored out into a pluggable strategy. You still have the proper MVC (or MVP, etc etc) encapsulation in which the details of the UI are kept private to the view-controller (here it's really a presenter, but who's counting...) pair. In other words the strategy knows nothing about the rest of the view, which is as it should be.
I'm developing a GUI application using Java8 and JavaFX. The main window has a button that should open new window (with it's own fxml). So far I've been loading the fxml each time the button was pressed but since the new window has tons of controls it (surprisingly) takes some time (aprox 0.5-1s) to open the popup, and thus I've changed the code so that the main controller loads the popup fxml in it's initialize method and whenever the button is clicked the pre loaded window is just shown.
It all works good but now I can't set the initOwner(...) on the new window since I don't have access to the window object in the initilize method. I know I don't have to set the initOwner, but then I have two application windows on the start menu (which I want to avoid). Any ideas how to go around this issue?
Also, what is the standard way of showing new windows/dialogs in JavaFX, should i load an fxml each time an event occurs or just show/hide the preloaded one?
You could load the FXML once in the initialize() method, then lazily initialize the dialog window when you need it. I.e.
public class Controller {
private Parent dialogPane ;
private Stage dialog ;
#FXML
private Button button ;
public void initialize() throws IOException {
dialogPane = FXMLLoader.load(getClass().getResource("dialog.fxml"));
}
#FXML
private void handleButtonPress() {
getDialog().show();
}
private Stage getDialog() {
if (dialog == null) {
Scene scene = new Scene(dialogPane);
dialog = new Stage();
dialog.setScene(scene);
dialog.initOwner(button.getScene().getWindow());
}
return dialog ;
}
}
You can lazy load the fxml. Namely not when app starts or not on every button click, but when it is requested:
private Parent popupPane;
private PopupPaneController popupPaneController;
private void openPopup( ActionEvent event ) {
if (popupPane == null) {
popupPane = FXMLLoader.load(...);
popupPaneController = // get it from FXMLLoader. Search this site for how
}
popupPaneController.updatePopupContent(newVals);
Stage popup = new Stage();
popup.initOwner(primaryStage);
stage.setScene(new ScrollPane(popupPane));
stage.show();
}
Note that if you cache the content of popup window, you can set initOwner() later. Also checkout the Alert class as an alternative for popup. See examples of it.
While showing the preloaded scene/window you may need to update the data shown in the popup. for this implement an updatePopupContent(newVals) method in popup controller's class, and call it on every button click as in code above.
I have a Chat application. I'd like the cursor in the chatTextArea to get back to the position 0 of the TextArea chatTextArea.
This, however, won't work:
chatTextArea.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent ke) {
if (ke.getCode().equals(KeyCode.ENTER)) {
ChatClient.main(new String[]{"localhost", String.valueOf(4444), chatTextArea.getText()});
chatTextArea.setText("");
chatTextArea.positionCaret(0);
}
}
});
How can I get it to work? Thank you.
The TextArea internally does not use the onKeyPressed property to handle keyboard input. Therefore, setting onKeyPressed does not remove the original event handler.
To prevent TextArea's internal handler for the Enter key, you need to add an event filter that consumes the event:
chatTextArea.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent ke) {
if (ke.getCode().equals(KeyCode.ENTER)) {
ChatClient.main(new String[]{"localhost", String.valueOf(4444), chatTextArea.getText()});
chatTextArea.setText("");
// chatTextArea.positionCaret(0); // not necessary
ke.consume(); // necessary to prevent event handlers for this event
}
}
});
Event filter uses the same EventHandler interface. The difference is only that it is called before any event handler. If an event filter consumes the event, no event handlers are fired for that event.