Problem with JavaFX pane.getChildren().remove() at lambda - java

I have written a code:
public class MousePosition extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
Text text = new Text();
Scene scene = new Scene(pane, 300, 100);
primaryStage.setScene(scene);
primaryStage.setTitle("Exercise 8");
primaryStage.show();
scene.setOnMouseReleased(e -> pane.getChildren().clear());
scene.setOnMouseClicked(e -> {
showCoordinates(pane, text, e);
});
scene.setOnMousePressed(e -> {
showCoordinates(pane, text, e);
});
}
private void showCoordinates(Pane pane, Text text, MouseEvent e) {
text.setText("(" + e.getX() + ", " + e.getY() + ")");
text.setX(e.getX());
text.setY(e.getY());
if (!pane.getChildren().contains(text))
pane.getChildren().add(text);
}
}
It should:
display the mouse position when the mouse button is clicked (see
Figure 15.28a), and the other displays the mouse position when the
mouse button is pressed and ceases to display it when the mouse button
is released.
But my lambda doesn't work:
scene.setOnMouseReleased(e -> pane.getChildren().clear());
It simply does nothing.
When I changed it to scene.setOnMouseReleased(e -> text.setText("")); It also does nothing. But when I use scene.setOnMouseReleased(e -> System.out.println("0"));, it works.

please try to explicitly update the UI component(s) in your eventhandler on the javafx application thread:
scene.setOnMouseReleased(e -> Platform.runLater(() -> pane.getChildren().clear()));
https://docs.oracle.com/javase/8/javafx/api/javafx/application/Platform.html#runLater-java.lang.Runnable-

Related

how to mouse event entered and exited pane change color label java fxml controller

i want to change color my label "Kategori" when mouse pressed and exit on "paneKategori" (Look at the picture).
my code in fxml controller
#FXML
void btnProdukMouseEntered(javafx.scene.input.MouseEvent event) {
if ( event.getSource() == paneKategori) {
labelKategori.setStyle("-fx-background-color: #FF0000;");
}
}
that is no working for me.
this my GUI in scene builder.
Create handlers for the MOUSE_PRESSED, MOUSE_DRAGGED and MOUSE_RELEASED handlers. Check, if the event's location is inside the node in MOUSE_DRAGGED.
Example
#Override
public void start(Stage primaryStage) throws Exception {
Button button = new Button("Drag me");
final String style = "-fx-background-color: red;";
button.setOnMousePressed(evt -> {
if (evt.isPrimaryButtonDown()) {
button.setStyle(style);
}
});
button.setOnMouseDragged(evt -> {
if (evt.isPrimaryButtonDown()) {
button.setStyle(button.contains(evt.getX(), evt.getY()) ? style : null);
}
});
button.setOnMouseReleased(evt -> {
button.setStyle(null);
});
Scene scene = new Scene(new StackPane(button), 300, 300);
primaryStage.setScene(scene);
primaryStage.show();
}
BTW: Checking the source in an event handler is bad practice in most cases, since you could easily avoid the check by only registering the event handler for the node you're comparing the source property of the event to.

Get visible state of Node in JavaFX 8

I need to detect if a node is currently displaying.
I.e. if my Node is in a TabPane, I need to know if it is in a selected tab or not.
In the example, I want to know when the HBox is displaying.The visibleProperty and managedProperty of Node, does not seem to help me:
public class VisibleTest extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TabPane tabpane = new TabPane();
tabpane.getTabs().add(new Tab("Tab1", new Label("Label1")));
HBox hbox = new HBox(new Label("Label2"));
hbox.setStyle("-fx-background-color: aquamarine;");
hbox.visibleProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("Hbox visible changed. newValue: " + newValue);
});
hbox.managedProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("Hbox managed changed. newValue: " + newValue);
});
Tab tab2 = new Tab("tab2", hbox);
tabpane.getTabs().add(tab2);
primaryStage.setScene(new Scene(tabpane));
primaryStage.setWidth(600);
primaryStage.setHeight(500);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I know, it is possible to listen on the selectedProperty state of the tab, but this does not solve my real problem.
Node.impl_isTreeVisible() does what I want, but this is depricated API.
Any ideas?
------------------------------------ update--------------------
I realize the code example above does not explain well what I'm trying to accomplish. Below is some Swing code that kind of demonstrates what I am trying to accomplish in JavaFX. Detect if the JComponent/Node is visible/shown, and based on that state, start or stop background processes. How would the constructor look like if it was a javaFX class.
public class SwingVisible extends JComponent {
String instanceNR;
Thread instanceThread;
boolean doExpensiveStuff = false;
public SwingVisible(String instanceNR) {
this.instanceNR = instanceNR;
this.setLayout(new FlowLayout());
this.add(new JLabel(instanceNR));
instanceThread = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (doExpensiveStuff) {
/*
* do expensive stuff.
*/
System.out.println(instanceNR + " is visible " + isVisible());
}
}
}
});
/*
* How to do this in FX?
*/
addComponentListener(new ComponentAdapter() {
#Override
public void componentShown(ComponentEvent e) {
if (!instanceThread.isAlive()) {
instanceThread.start();
}
doExpensiveStuff = true;
}
#Override
public void componentHidden(ComponentEvent e) {
doExpensiveStuff = false;
}
});
}
public static void main(String[] args) {
/*
* This block represents code that is external to my library. End user
* can put instances of SwingVisible in JTabbedPanes, JFrames, JWindows,
* or other JComponents. How many instances there will bee is not in my
* control.
*/
JTabbedPane jtp = new JTabbedPane();
jtp.add("tab1", new SwingVisible("1"));
jtp.add("tab2", new SwingVisible("2"));
jtp.add("tab3", new SwingVisible("3"));
JFrame f = new JFrame("test");
f.setContentPane(jtp);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300, 300);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
Output when tab1 is selected:
1 is visible true
1 is visible true
1 is visible true
...
Output when tab2 is selected:
2 is visible true
2 is visible true
2 is visible true
...
You can use Tab's selectedProperty to know if it is selected or not, and by extension if its content is visible or not. It is a boolean property.
I've converted your Swing code to JavaFX based on your initial JavaFX example:
public class VisibleTest extends Application {
public class FXVisible extends Tab {
FXVisible(String id) {
super(id, new Label(id));
Timeline thread = new Timeline(
new KeyFrame(Duration.ZERO, e -> {
if (isSelected()) {
// do expensive stuff
System.out.println(id + " is visible");
}
}),
new KeyFrame(Duration.seconds(1))
);
thread.setCycleCount(Timeline.INDEFINITE);
selectedProperty().addListener((selectedProperty, wasSelected, isSelected) -> {
if (isSelected) {
if (thread.getStatus() != Status.RUNNING) {
System.out.println(id + " starting thread");
thread.play();
}
}
// else, it is not selected -> content not shown
});
}
}
#Override
public void start(Stage primaryStage) throws Exception {
TabPane tabpane = new TabPane();
tabpane.getTabs().add(new FXVisible("1"));
tabpane.getTabs().add(new FXVisible("2"));
tabpane.getTabs().add(new FXVisible("3"));
// add as many as you want
primaryStage.setScene(new Scene(tabpane));
primaryStage.setWidth(600);
primaryStage.setHeight(500);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I replaced your thread with a JavaFX Timeline. Your question is not about this topic so I won't go into details here, though it's self explanatory.
I don't understand why in the Swing example you have a listener changing a boolean that indicates if the component is visible or not when you can just call isVisible() directly in the thread (see comments below for a note about threading). This is why in my code above I took the approach of checking isSelected() directly with no self-declared boolean. If you need to revert to your design it's rather straightforward. Just noting this for clarity.
The ComponentListener can be replaced with a change listener on selectedProperty() and querying the new value. Just be sure that your example does what it's supposed to do: the first time the tab is selected the thread/timer starts. After that the thread/timer does nothing. You might have wanted to pause the computation for non-displaying content. Again, just noting it because it seemed like a potential mistake to me, otherwise you're fine.
Updated answer.
tab2.getContent().isVisible();
It seems to me that my original answer is correct. If not, you need to ask your question in a better way. You want to know when the hbox is visible(meaning you can see the hbox on the screen).
tabpane.getSelectionModel().selectedItemProperty().addListener((obsVal, oldTab, newTab)->{
System.out.println(newTab.getText());
if(newTab.getText().equals("tab2"))
{
//You can use this code to set the hbox visibility, that way you can force the behavior you are looking for.
hbox.setVisible(true);
System.out.println("hbox is visible!");
}
else
{
//You can use this code to set the hbox visibility, that way you can force the behavior you are looking for.
hbox.setVisible(false);
System.out.println("hbox is not visible!");
}
});
From quick checking this seemed to work for both checking the window is showing and that the tab it is in is displaying. I have also checked and it seems to work as expected for titled panes too that are collapsible.
public static boolean detectVisible( Node node )
{
Node current = node;
while( current != null ) {
if( !current.isVisible() ) {
return false;
}
current = current.getParent();
}
Window window = Optional.of( node ).map( Node::getScene ).map( Scene::getWindow ).orElse( null );
if( window == null ) {
return false;
}
if( window instanceof Stage && ( (Stage) window ).isIconified() ) {
return false;
}
return window.isShowing();
}

How to change the position of a RadioButton's label?

By default RadioButtons have their text label to the right of the button. I want the label to appear below the button instead. I found an old discussion on the Oracle forums but the solutions aren't great or just don't work.
I can create a custom component with a text-less radio button and a separate text label and position them as in a VBox. But then only the button itself responds to user events and not the whole thing.
Is there no simple way to reposition the label?
There is no "simple" way to do this (simple means setting a single property or something like this).
As a workaround you could do something like you mentioned with a VBox, but with a Label: You can set the RadioButton as the graphic of the Label and set the contentDisplayProperty to TOP (RadioButton is placed on top of the Label). And then you can add an event handler on the Label to select the RadioButton on click.
An example with this approach
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 400, 400);
HBox hbox = new HBox();
hbox.getChildren().add(createRadioLabel("Radio on the left", ContentDisplay.LEFT));
hbox.getChildren().add(createRadioLabel("Radio on the top", ContentDisplay.TOP));
hbox.getChildren().add(createRadioLabel("Radio on the bottom", ContentDisplay.BOTTOM));
hbox.getChildren().add(createRadioLabel("Radio on the right", ContentDisplay.RIGHT));
hbox.setSpacing(30);
root.setCenter(hbox);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
private Label createRadioLabel(String text, ContentDisplay cd) {
Label label = new Label(text);
label.setGraphic(new RadioButton());
label.setContentDisplay(cd);
label.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> {
RadioButton radioButton = (RadioButton) ((Label) e.getSource()).getGraphic();
radioButton.requestFocus();
radioButton.setSelected(!radioButton.isSelected());
});
return label;
}
public static void main(String[] args) {
launch(args);
}
}
And the produced RadioButtons:
Alternatively, if you want to have the text of the RadioButton rotated around the dot, you can use CSS rotations, with the attribute -fx-rotate:
.radio-button { -fx-rotate:180; }
.radio-button > .text { -fx-rotate: 180; }
The first selector will rotate the whole RadioButton, so as result the text will be placed on the left side of the "dot", upside down. The second selector rotates the text back to the normal direction.
Example
This example shows a RadioButton whose Text can be placed to any side of the "dot" specified by a ComboBox selection.
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 400, 400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
RadioButton rb = new RadioButton("In all directions);
ComboBox<PseudoClass> combo = new ComboBox<>();
combo.getItems().addAll(PseudoClass.getPseudoClass("left"),
PseudoClass.getPseudoClass("top"),
PseudoClass.getPseudoClass("right"),
PseudoClass.getPseudoClass("bottom"));
combo.valueProperty().addListener((obs, oldVal, newVal) -> {
if (oldVal != null)
rb.pseudoClassStateChanged(oldVal, false);
rb.pseudoClassStateChanged(newVal, true);
});
root.setTop(combo);
root.setCenter(rb);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
application.css
.radio-button:left > .text { -fx-rotate: 180; }
.radio-button:left { -fx-rotate:180; }
.radio-button:right > .text { -fx-rotate: 0; }
.radio-button:right { -fx-rotate:0; }
.radio-button:top > .text { -fx-rotate: 0; }
.radio-button:top { -fx-rotate:-90; }
.radio-button:bottom > .text { -fx-rotate: 0; }
.radio-button:bottom { -fx-rotate:90; }
And the displayed RadioButton:

JavaFX add Mouse Listener to TreeCell Graphic

I'm trying to add a MouseClicked event listener to a Tree Cell so that It fires when a user clicks on either the Graphic or the String value of the cell.
TestApp.java
public class TestApp extends Application {
#Override
public void start(Stage stage) throws Exception {
Image image = new Image(getClass().getResourceAsStream("icon.png"));
TreeItem<String> root = new TreeItem<String>("Root");
root.setGraphic(new ImageView(image));
TreeItem<String> child = new TreeItem<String>("Child");
root.getChildren().add(child);
TreeView<String> tree = new TreeView<String>(root);
tree.setCellFactory(new TestTreeCellFactory());
StackPane pane = new StackPane();
pane.getChildren().add(tree);
stage.setScene(new Scene(pane, 300, 250));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
private class TestTreeCellFactory implements Callback<TreeView<String>, TreeCell<String>> {
#Override
public TreeCell<String> call(TreeView<String> treeView) {
CheckBoxTreeCell<String> tc = new CheckBoxTreeCell<String>();
tc.setOnMouseClicked((event) -> mousePressed(event, treeView));
return tc;
}
public void mousePressed(MouseEvent event, TreeView<String> treeView) {
if(event.getButton().equals(MouseButton.SECONDARY)) {
System.out.println("Right Mouse Button Clicked!");
}
}
}
}
The mouse event is currently firing when I click on the String Value but is not firing when I click on the Graphic icon. Upon trying to debug the issue, here are some of my observations:
It seems like the Graphic is somehow tied to the Checkbox (clicking on the Graphic will toggle the checkbox value).
Clicking the Graphic will not select the row while clicking on the text value will.
Is there a way to attach the listener to the Tree Cell's graphic once it gets populated or am I thinking about this in the wrong way?

Intercept Paste Event on JavaFx HTMLEditor

Hello I want to know how to intercept the Paste Event in the JavaFX HTMLEditor.
You can't. The HTMLEditor uses a WebPage internally. Basically during a paste event it sends a "paste" command via
private boolean executeCommand(String command, String value) {
return webPage.executeCommand(command, value);
}
and then a
twkExecuteCommand(getPage(), command, value);
However, you could intercept everything that implicitly invokes the paste event like the button click or a CTRL+V key combination and depending on what you wish to do consume the event.
Example:
public class HTMLEditorSample extends Application {
#Override
public void start(Stage stage) {
final HTMLEditor htmlEditor = new HTMLEditor();
Scene scene = new Scene(htmlEditor, 800, 600);
stage.setScene(scene);
stage.show();
Button button = (Button) htmlEditor.lookup(".html-editor-paste");
button.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {
System.out.println("paste pressed");
// e.consume();
});
htmlEditor.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
if( e.isControlDown() && e.getCode() == KeyCode.V) {
System.out.println( "CTRL+V pressed");
// e.consume();
}
});
}
public static void main(String[] args) {
launch(args);
}
}
As to your other question with pasting only plain text to the html editor, you could do it like this:
public class HTMLEditorSample extends Application {
#Override
public void start(Stage stage) {
final HTMLEditor htmlEditor = new HTMLEditor();
Scene scene = new Scene(htmlEditor, 800, 600);
stage.setScene(scene);
stage.show();
Button button = (Button) htmlEditor.lookup(".html-editor-paste");
button.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {
modifyClipboard();
});
htmlEditor.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
if( e.isControlDown() && e.getCode() == KeyCode.V) {
modifyClipboard();
}
});
}
private void modifyClipboard() {
Clipboard clipboard = Clipboard.getSystemClipboard();
String plainText = clipboard.getString();
ClipboardContent content = new ClipboardContent();
content.putString(plainText);
clipboard.setContent(content);
}
public static void main(String[] args) {
launch(args);
}
}
It's a workaround and ugly because you should never modify the clipboard content unless the user wants to, but it works. On the other hand, it may be possible to revert the clipboard content back to its original state after the paste operation.
Edit:
Here's how you could access the contextmenu, e. g. for disabling it:
WebView webView = (WebView) htmlEditor.lookup(".web-view");
webView.setContextMenuEnabled(false);
Don't try this line, because it's always return a NULL:
Button button = (Button) htmlEditor.lookup(".html-editor-paste");
The only way to get the Paste button from HTMLEditor is the solution from #taha:
htmlEditor.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> { if (e.getTarget().toString().contains("html-editor-paste")) { System.out.println("paste pressed"); });

Categories