JavaFX: Mouse Hover event for a PopOver (ControlsFX) - javafx

I am having the following code to display a PopOver
#Override
public void start(Stage primaryStage) {
try {
Label lblName = new Label("Tetsing name");
Label lblStreet = new Label("Some street name");
Label lblCityStateZip = new Label("Some city, 111111");
VBox vBox = new VBox(lblName, lblStreet, lblCityStateZip);
PopOver popOver = new PopOver(vBox);
Label label = new Label("Mouse mouse over me");
label.setOnMouseEntered(mouseEvent -> {
popOver.show(label, -3);
});
label.setOnMouseExited(mouseEvent -> {
if (popOver.isShowing()) {
popOver.hide();
}
});
StackPane root = new StackPane();
root.getChildren().add(label);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.setOnCloseRequest((WindowEvent event) -> {
System.exit(0);
});
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
The problem is ,
I want the pop-up to be displayed when mouse entered the Label - works fine.
I want the pop-up to be hidden when user exits mouse from Label but not if he enters mouse in to the pop-up window.
I have added MouseEntered and MouseExited actions on Label but how can i handle the another scenario where i don't want to hide the pop-up if user enters mouse in to pop-up.

Related

JavaFX : Mouse events for a PopOver Window (ControlsFX)

I am having the following code to display a PopOver (Custom PopUp by ControlsFX - mvn repo)
public class JavaFXApplication35 extends Application {
#Override
public void start(Stage primaryStage) {
try {
Label lblName = new Label("Tetsing name");
Label lblStreet = new Label("Some street name");
Label lblCityStateZip = new Label("Some city, 111111");
VBox vBox = new VBox(lblName, lblStreet, lblCityStateZip);
PopOver popOver = new PopOver(vBox);
Label label = new Label("Mouse mouse over me");
label.setOnMouseEntered(mouseEvent -> {
popOver.show(label, -3);
});
label.setOnMouseExited(mouseEvent -> {
if (popOver.isShowing()) {
popOver.hide();
}
});
StackPane root = new StackPane();
root.getChildren().add(label);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.setOnCloseRequest((WindowEvent event) -> {
System.exit(0);
});
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
The problem is ,
I want the pop-up to be displayed when mouse entered the Label - works fine.
I want the pop-up to be hidden when user exits mouse from Label but not if he enters mouse in to the pop-up window.
I have added MouseEntered and MouseExited actions on Label but how can i handle the another scenario where i don't want to hide the pop-up if user enters mouse in to pop-up.
I ran into the same problem. Here is my solution. Just pass your label (or other node) and PopOver's content node as arguments to this method.
public static void addAutoHidingPopOver(Node hoverableNode, Node contentNode) {
//Creating PopOver
PopOver popOver = new PopOver(hoverableNode);
popOver.setContentNode(contentNode);
//Here you can set custom parameters of your PopOver
//...
//Mouse Actions handling
final Timeline timeline = new Timeline();
timeline.getKeyFrames().add(new KeyFrame(Duration.millis(1000)));
timeline.setOnFinished(finishEvent -> {
if (hoverableNode.isHover() || contentNode.isHover()) timeline.play();
else popOver.hide();
});
hoverableNode.setOnMouseEntered(mouseEvent -> {if (!popOver.isShowing()) popOver.show(hoverableNode);});
hoverableNode.setOnMouseExited(mouseEvent -> timeline.play());
}
PopOver will be hidden after 1 sec after mouse leave hoverableNode or contentNode. Use it like this:
addAutoHidingPopOver(someLabel, someContentNode);
Note, that your content node should take all visible space of PopOver for comfort use.
That could be expected behavior. I am not sure, but here is a workaround. You can use a ToggleButton.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.controlsfx.control.PopOver;
public class App extends Application
{
#Override
public void start(Stage primaryStage)
{
//Build PopOver look and feel
Label lblName = new Label("John Doe");
Label lblStreet = new Label("123 Hello Street");
Button lblCityStateZip = new Button("MadeUpCity, XX 55555");
VBox vBox = new VBox(lblName, lblStreet, lblCityStateZip);
//Create PopOver and add look and feel
PopOver popOver = new PopOver(vBox);
ToggleButton toggleButton = new ToggleButton("Click me!");
toggleButton.selectedProperty().addListener((obs, oldValue, newValue) -> {
if (newValue) {
popOver.show(toggleButton);
}
else {
popOver.hide();
}
});
;
StackPane root = new StackPane();
root.getChildren().add(toggleButton);
var scene = new Scene(root, 500, 500);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}

How to disable right click on a menu in javafx

In javaFX code, a menu can popup by left click or right click. How to disable right click?
public void start(Stage primaryStage)
{
BorderPane root = new BorderPane();
MenuBar menuBar = new MenuBar();
Menu hello = new Menu("hello");
menuBar.getMenus().addAll(hello);
Menu world = new Menu("world");
menuBar.getMenus().addAll(world);
root.setCenter(menuBar);
MenuItem item = new MenuItem("laugh");
hello.getItems().add(item);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
When I right click the "hello" menu, it will popup menuitem "laugh".
The basic approach is to register a eventFilter on the MenuBar that consumes the events that should not be delivered to the children.
Doing so manually in your application code:
public class DisableRightClickOpenMenu extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
MenuBar menuBar = new MenuBar();
menuBar.addEventFilter(MouseEvent.MOUSE_PRESSED, ev -> {
if (ev.getButton() == MouseButton.SECONDARY) {
ev.consume();
}
});
Menu hello = new Menu("hello");
menuBar.getMenus().addAll(hello);
Menu world = new Menu("world");
menuBar.getMenus().addAll(world);
root.setCenter(menuBar);
MenuItem item = new MenuItem("laugh");
hello.getItems().add(item);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If you want this behaviour across all your applications, you can implement a custom menuBarSkin that registers the filter and install the custom skin via a stylesheet.
The skin:
public class ExMenuBarSkin extends MenuBarSkin {
/**
* Instantiates a skin for the given MenuBar. Registers an
* event filter that consumes right mouse press.
*
* #param menuBar
*/
public ExMenuBarSkin(MenuBar menuBar) {
super(menuBar);
menuBar.addEventFilter(MouseEvent.MOUSE_PRESSED, ev -> {
if (ev.getButton() == MouseButton.SECONDARY) {
ev.consume();
}
});
}
}
In your stylesheet (replace with your fully qualified class name):
.menu-bar {
-fx-skin: "de.swingempire.fx.event.ExMenuBarSkin";
}
Its usage (replace the name with your stylesheet file name):
URL uri = getClass().getResource("contextskin.css");
primaryStage.getScene().getStylesheets().add(uri.toExternalForm());
This is usual behavior of menu in many programs. I don't think you can change it. However, you can use some other controls and simulate menu. (Like HBox and Labels).
I agree as far as I know there's no a standard way to do this, but you may want to consider the following workaround.
It is replacing the Menu node with a Menu object composed by an HBox and a Label: an EventHandler is added to the HBox and by checking the mouse button pressed we add/remove on the fly the MenuItem to its parent.
#Override
public void start(final Stage primaryStage) {
final BorderPane root = new BorderPane();
final MenuBar menuBar = new MenuBar();
final Menu menuHello = new Menu();
final Menu menuWorld = new Menu("world");
final MenuItem menuitem = new MenuItem("laugh");
final HBox hbox = new HBox();
menuBar.getMenus().addAll(menuHello, menuWorld);
root.setCenter(menuBar);
hbox.setPrefWidth(30);
hbox.getChildren().add(new Label("hello"));
menuHello.setGraphic(hbox);
menuHello.getItems().add(menuitem);
hbox.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(final MouseEvent e) {
if (e.getButton() == MouseButton.SECONDARY) {
System.out.println("Right click");
menuHello.getItems().remove(menuitem);
} else {
System.out.println("Left click");
if (!menuHello.getItems().contains(menuitem)) {
menuHello.getItems().add(menuitem);
menuHello.show(); // The .show method prevent 'losing' the current click }
}
}
});
final Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
This will produce the following result - preview
Note that I've used an HBox just for habit, there's no a particular reason.
While using a workaround like this, my suggestion would be to fill all the Menus with the same 'pattern', such as the HBox + Label combo in my example, and stylize them via css/code (width/height, background/fill/hover... colors etc.) in order to have them as uniform as possible and avoid creating graphic inconsistencies due to have different nodes types in the same menubar.

How to get data from Tab in Tabpane JavaFX

I want get data from Tab in Tabpane JavaFX
I have 2 Tab in Tabpane, And each Tab I have a TextArea, I want click Button will get data from 2 tab
Here's my code:
btnThem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
try {
i++;
FXMLLoader fxmlLoader = new FXMLLoader(
getClass().getResource("/fxml/tab.fxml"));
Parent parent = (Parent) fxmlLoader.load();
Tab tab = new Tab("Điểm " + i);
tab.setContent(parent);
tab.setClosable(true);
tabPane.getTabs().add(tab);
controllerTab = (ControllerTab) fxmlLoader.getController();
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
});
Your question is ambiguous but it seems you want to get data from each textArea in tab.To do this you should get nodes (children) from each tab and by using lookup() we confirm and we parse node to textArea.I tried to figure your scene and made this example to help you :
public class example extends Application {
TextArea textArea = new TextArea();
TextArea textArea1 = new TextArea();
Button button = new Button("button");
#Override
public void start(Stage primaryStage) {
Tab tab1 = new Tab("tab1");
Tab tab2 = new Tab("tab2");
tab1.setContent(textArea);
tab2.setContent(textArea1);
TabPane pane = new TabPane();
pane.getTabs().addAll(tab1, tab2);
Node node1 = tab1.getContent();
Node node2 = tab2.getContent();
button.setOnAction((ActionEvent event) -> {
if (node1.lookup("TextArea") != null && node2.lookup("TextArea") != null) {
TextArea area1 = (TextArea) node1.lookup("TextArea");
TextArea area2 = (TextArea) node2.lookup("TextArea");
System.out.println(area1.getText() + " " + area2.getText());
}
});
VBox root = new VBox();
root.setAlignment(Pos.TOP_RIGHT);
root.getChildren().add(pane);
root.getChildren().add(button);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
And you can see the result :
Hello ,tab1 Hello ,tab2
Deleting directory C:\Users\Electron\Documents\NetBeansProjects\Buttono\dist\run341573612
jfxsa-run:
BUILD SUCCESSFUL (total time: 22 seconds)

JavaFX Transition - Darken button on hover

I'm a beginner in JavaFX. I'm trying to create my own Button subclass that would have its on animations for mouse enter and mouse exit. The animation I'm trying to achieve is a simple "darken" or "dim" transition that would darken the color of the button background when user hovers over the button , and would animate back to normal state when the mouse exits the button.
First I thought I can achieve this with FillTransition, but for that I would need the specific darker color of the button, that depends on the button color.
So now I'm trying to basically fade in and fade out a low-opacity black rectangle on top of the button, but the rectangle doesn't seem to appear at all.
Here's the code of my button:
public class FlatButton extends Button {
private Rectangle dimRectangle;
private Duration dimDuration = Duration.millis(250);
private Color dimColor = new Color(0,0,0,0.11);
public FlatButton(String text) {
super(text);
getStyleClass().addAll("flat-button-style");
createEffect();
}
private void createEffect()
{
dimRectangle = new Rectangle(this.getWidth(), this.getHeight(), dimColor);
dimRectangle.setOpacity(1.0);
dimRectangle.setX(this.get);
FadeTransition enterTransition = new FadeTransition(dimDuration, this);
enterTransition.setInterpolator(Interpolator.EASE_OUT);
enterTransition.setFromValue(0.0);
enterTransition.setToValue(1.0);
FadeTransition exitTransition = new FadeTransition(dimDuration, this);
exitTransition.setInterpolator(Interpolator.EASE_OUT);
exitTransition.setFromValue(1.0);
exitTransition.setToValue(0.0);
this.setOnMouseEntered(new EventHandler<MouseEvent>(){
public void handle(MouseEvent mouseEvent){
enterTransition.play();
}
});
this.setOnMouseExited(new EventHandler<MouseEvent>(){
public void handle(MouseEvent mouseEvent){
exitTransition.play();
}
});
}
}
EDIT: The part in the code "new FadeTransition(dimDuration, this);" should be "new FadeTransition(dimDuration, dimRectangle);". It's just something I was testing.
EDIT2: I figured that "dimRectangle = new Rectangle(this.getWidth(), this.getHeight(), dimColor);" is not really working , but I havent found a way yet how to make the rectangle fill the button dimensions.
You could use a ColorAdjust effect and change it's brightness property using a Timeline.
public class ButtonFadeDemo extends Application {
#Override
public void start(Stage primaryStage) {
try {
Pane root = new Pane();
Button button = new Button("Click me!");
ColorAdjust colorAdjust = new ColorAdjust();
colorAdjust.setBrightness(0.0);
button.setEffect(colorAdjust);
button.setOnMouseEntered(e -> {
Timeline fadeInTimeline = new Timeline(
new KeyFrame(Duration.seconds(0),
new KeyValue(colorAdjust.brightnessProperty(), colorAdjust.brightnessProperty().getValue(), Interpolator.LINEAR)),
new KeyFrame(Duration.seconds(1), new KeyValue(colorAdjust.brightnessProperty(), -1, Interpolator.LINEAR)
));
fadeInTimeline.setCycleCount(1);
fadeInTimeline.setAutoReverse(false);
fadeInTimeline.play();
});
button.setOnMouseExited(e -> {
Timeline fadeOutTimeline = new Timeline(
new KeyFrame(Duration.seconds(0),
new KeyValue(colorAdjust.brightnessProperty(), colorAdjust.brightnessProperty().getValue(), Interpolator.LINEAR)),
new KeyFrame(Duration.seconds(1), new KeyValue(colorAdjust.brightnessProperty(), 0, Interpolator.LINEAR)
));
fadeOutTimeline.setCycleCount(1);
fadeOutTimeline.setAutoReverse(false);
fadeOutTimeline.play();
});
root.getChildren().addAll(button);
Scene scene = new Scene(root, 800, 400);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}

Set image on left side of dialog

I created this very simple example for JavaFX alert dialog for JavaFX8u40.
public class MainApp extends Application
{
public static void main(String[] args)
{
Application.launch(args);
}
private Stage stage;
#Override
public void start(Stage primaryStage) throws Exception
{
Button create = new Button("Create Alert");
create.setTooltip(new Tooltip("Create an Alert Dialog"));
create.setOnAction(e ->
{
createAlert();
});
primaryStage.setScene(new Scene(create));
primaryStage.show();
stage = primaryStage;
}
protected Alert createAlert()
{
Alert alert = new Alert(AlertType.WARNING);
Image image1 = new Image("http://www.mcaprojecttraining.com/images/java-big-icon.png");
ImageView imageView = new ImageView(image1);
alert.setGraphic(imageView);
alert.initModality(Modality.APPLICATION_MODAL);
alert.initOwner(stage);
alert.getDialogPane().setContentText("Some text");
alert.showAndWait()
.filter(response -> response == ButtonType.OK)
.ifPresent(response -> System.out.println("The alert was approved"));
return alert;
}
}
I'm interested how I can set the image on the left side of the dialog.
Did someone manage to change the side of the image?
If you have a look at how the header is constructed, you'll find a GridPane node to layout a Label on the left and a StackPane for the icon.
If you want to reverse the cells order by code, you can do it, but it will be overriden every time updateHeaderArea() is called.
My suggestion is using this public API:
dialogPane.setHeader(Node header);
dialogPane.setGraphic(Node graphic);
providing a header with an icon on the left and a label, and a null graphic.
Using the same approach as DialogPane, we could add another GridPane as header:
protected Alert createAlert(){
Alert alert = new Alert(AlertType.WARNING);
alert.initModality(Modality.APPLICATION_MODAL);
alert.initOwner(stage);
alert.getDialogPane().setContentText("Some text");
DialogPane dialogPane = alert.getDialogPane();
GridPane grid = new GridPane();
ColumnConstraints graphicColumn = new ColumnConstraints();
graphicColumn.setFillWidth(false);
graphicColumn.setHgrow(Priority.NEVER);
ColumnConstraints textColumn = new ColumnConstraints();
textColumn.setFillWidth(true);
textColumn.setHgrow(Priority.ALWAYS);
grid.getColumnConstraints().setAll(graphicColumn, textColumn);
grid.setPadding(new Insets(5));
Image image1 = new Image("http://www.mcaprojecttraining.com/images/java-big-icon.png");
ImageView imageView = new ImageView(image1);
imageView.setFitWidth(64);
imageView.setFitHeight(64);
StackPane stackPane = new StackPane(imageView);
stackPane.setAlignment(Pos.CENTER);
grid.add(stackPane, 0, 0);
Label headerLabel = new Label("Warning");
headerLabel.setWrapText(true);
headerLabel.setAlignment(Pos.CENTER_RIGHT);
headerLabel.setMaxWidth(Double.MAX_VALUE);
headerLabel.setMaxHeight(Double.MAX_VALUE);
grid.add(headerLabel, 1, 0);
dialogPane.setHeader(grid);
dialogPane.setGraphic(null);
alert.showAndWait()
.filter(response -> response == ButtonType.OK)
.ifPresent(response -> System.out.println("The alert was approved"));
return alert;
}
And this is what you will see:

Resources