SplitPane get selected pane - java

I have a splitpane with each containing a anchorpane with a tableview (paneA paneB). By clicking on the button "Show" I want to open a new view depending on the selected side of the split pane.
E.G.
Pane A | Pane B
patient 1 | patient a
patient 2 | patient b
(ShowButon)
What I imagine.
private void showButton(ActionEvent e) {
if (is selected paneA){
get selected row
open view conataining information from selected row paneA
else if (is selected paneB) {
get selected row
open view conaining information from selected row paneB
}
}
For a tab view for example you can easily get the selected tab. Now is something like this possible for a splitpane?
I hope it is now more understandable.
Thanks in advance

I do not know of any way to watch which side of a SplitPane has been clicked on, but you can certainly register a listener on the Node you've placed within each side.
The example below creates a very simple interface with a VBox in each of the two SplitPane sides. We simply listen for a click on either VBox and respond accordingly:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.SplitPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class PaneSelectionExample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
SplitPane splitPane = new SplitPane();
VBox.setVgrow(splitPane, Priority.ALWAYS);
// Two VBoxes with Labels
VBox box1 = new VBox() {{
setAlignment(Pos.TOP_CENTER);
getChildren().addAll(
new Label("One"),
new Label("Two"),
new Label("Three")
);
}};
VBox box2 = new VBox() {{
setAlignment(Pos.TOP_CENTER);
getChildren().addAll(
new Label("One"),
new Label("Two"),
new Label("Three")
);
}};
// Now, we'll add an EventListener to each child pane in the SplitPane to determine which
// has been clicked
box1.setOnMouseClicked(event -> System.out.println("Left Pane clicked!"));
box2.setOnMouseClicked(event -> System.out.println("Right Pane clicked!"));
// Add our VBoxes to the SplitPane
splitPane.getItems().addAll(box1, box2);
root.getChildren().add(splitPane);
// Show the Stage
primaryStage.setWidth(300);
primaryStage.setHeight(300);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
Incoming Opinion Alert
While this may solve your immediate question, you may want to revisit your decision to have only one Show button. Is the user going to expect that and understand which details the Show button will present?
It may be a better idea to have a separate Show button in each pane of the SplitPane; that seems more "standard" to me.

Related

Access a JavaFX TextArea within Tab returned from a TabPane

I would like to access a text area placed within a Tab. All the Tabs in the TabPane will have a TextArea. Unfortunately Tab is not an interface and there does not seem a way to change the type held inside of the TabPane so I can not see a way to make code know that there is going to be a TextArea inside of the generic tab without keeping a separate list of them somewhere outside. TabPane can only return a Tab and tab does not distinguish that it holds a TextArea and even if I make an extension of Tab and give it to the TabPane it will still only return a Tab. Keeping an outside list seems really hacky and I dont think that would ever be the intended design. So what am I missing here. I am not using FXML.
You could create your own extension of a Tab whose content is a TextArea and provide a getTextArea() method for it.
That way, you can know each Tab will have a TextArea and you can manipulate it however you would like.
Below is a simple application to demonstrate:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple UI
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
// Create the TabPane
TabPane tabPane = new TabPane();
// Create some TextAreaTabs to put into the TabPane
tabPane.getTabs().addAll(
new TextAreaTab("Tab 1", "This is tab 1!"),
new TextAreaTab("Tab 2", "This is tab 2!"),
new TextAreaTab("Tab 3", "This is tab 3!")
);
// A button to get the text from each tab
Button button = new Button("Print Text");
// Set the action for the button to loop through all the tabs in the TabPane and print the contents of its TextArea
button.setOnAction(e -> {
for (Tab tab : tabPane.getTabs()) {
// You'll need to cast the Tab to a TextAreaTab in order to access the getTextArea() method
System.out.println(tab.getText() + ": " +
((TextAreaTab) tab).getTextArea().getText());
}
});
// Add the TabPane and button to the root layout
root.getChildren().addAll(tabPane, button);
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setWidth(300);
primaryStage.setHeight(300);
primaryStage.show();
}
}
// This is the custom Tab that allows you to set its content to a TextArea
class TextAreaTab extends Tab {
private TextArea textArea = new TextArea();
public TextAreaTab(String tabTitle, String textAreaContent) {
// Create the tab with the title provided
super(tabTitle);
// Set the Tab to be unclosable
setClosable(false);
// Set the text for the TextArea
textArea.setText(textAreaContent);
// Set the TextArea as the content of this tab
setContent(textArea);
}
public TextArea getTextArea() {
return textArea;
}
}
A simpler option would be to simply cast the getContent() method for each Tab to a TextArea within your loop:
((Text Area) tab.getContent()).getText()
I find the first option to be more readable and flexible as you can configure all your tabs to be identical within one location.

Divide stage into 2 gridpanes JavaFX

So Im trying to have text on the left and buttons on the right, text should have constant size and buttons should resize to fill the rest of the window.
Here is my result so far:
I dont want my text over buttons, I want them to share the whole window.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
GridPane buttons = new GridPane();
GridPane textGrid = new GridPane();
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
Button button1 = new Button();
Button button2 = new Button();
Button button3 = new Button();
Button button4 = new Button();
Button button5 = new Button();
button1.setText("Button1");
button2.setText("Button4");
button3.setText("Button3");
button4.setText("Button4");
button5.setText("Button5");
TextArea text1 = new TextArea();
text1.setText("Test");
text1.setPrefSize(100, 100);
button1.prefWidthProperty().bind(buttons.widthProperty());
button2.prefWidthProperty().bind(buttons.widthProperty());
button3.prefWidthProperty().bind(buttons.widthProperty());
button4.prefWidthProperty().bind(buttons.widthProperty());
button5.prefWidthProperty().bind(buttons.widthProperty());
button1.prefHeightProperty().bind(buttons.heightProperty());
button2.prefHeightProperty().bind(buttons.heightProperty());
button3.prefHeightProperty().bind(buttons.heightProperty());
button4.prefHeightProperty().bind(buttons.heightProperty());
button5.prefHeightProperty().bind(buttons.heightProperty());
buttons.addColumn(0, button1, button2, button3, button4, button5);
textGrid.addColumn(0, text1);
Scene scene = new Scene(root, 280, 180);
root.getChildren().addAll(buttons, textGrid);
buttons.setAlignment(Pos.TOP_RIGHT);
textGrid.setAlignment(Pos.TOP_LEFT);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
It is usually better to let the layout panes handle the layout management rather than trying to manage the layout through bindings.
Here is a sample:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.util.stream.IntStream;
public class Main extends Application {
private static final int N_BUTTONS = 5;
#Override
public void start(Stage stage) {
VBox buttonLayout = new VBox(
10,
IntStream.range(0, N_BUTTONS)
.mapToObj(this::createButton)
.toArray(Button[]::new)
);
HBox.setHgrow(buttonLayout, Priority.ALWAYS);
TextArea textArea = new TextArea("Test");
textArea.setPrefWidth(100);
textArea.setMaxWidth(TextArea.USE_PREF_SIZE);
textArea.setMinWidth(TextArea.USE_PREF_SIZE);
HBox layout = new HBox(10, textArea, buttonLayout);
layout.setPadding(new Insets(10));
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
private Button createButton(int i) {
Button button = new Button("Button " + i);
// button.setMaxWidth(Double.MAX_VALUE);
button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
VBox.setVgrow(button, Priority.ALWAYS);
return button;
}
public static void main(String[] args) {
launch(args);
}
}
Here are a couple of things I would point out based upon the sample:
As the buttons are so similar, create the buttons in a loop rather than individually in code. I use an IntStream range with a map and a toArray, but you could do the same thing with a standard for loop (which may be easier to understand).
Use combinations of standard layout panes to achieve your layout. For example the buttons are vertically spaced, so put them in a VBox, the text and the buttons are horizontal to each other, so use a HBox.
Use constraints on the layouts to massage them into performing the layout you like, for example, HBox.setHgrow(buttonLayout, Priority.ALWAYS); tells the Box to always assign any extra additional space in the Box to the buttonLayout so that the buttons will fill any remaining area.
Set constraints on the individual nodes to size them how you wish, for example the following code establishes a fixed width for the textArea, which will not vary (you could similar code to establish a fixed height if you wished):
textArea.setPrefWidth(100);
textArea.setMaxWidth(TextArea.USE_PREF_SIZE);
textArea.setMinWidth(TextArea.USE_PREF_SIZE);
Some controls will automatically expand themselves beyond their max size, buttons do not by default, to enable this behavior use the following code (if you only wanted the width to expand and not the height then you would only set the maxWidth rather than the maxSize):
button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
Rather than defining layouts in code as in this example, instead use a tool such as SceneBuilder to create the scene visually and save the layout as an FXML file, so that the layout is separated from your code (similarly place any styling in an external CSS file).

NotificationPane doesn't show up in Scene

I would like to display a NotificationPane after certain user actions. My application has multiple scenes and the NotificationPane should be showed up in the currently active scene.
The whole thing works with Notification, it pops up when I need it.
But I can't figure out how to make this work for NotificationPane.
Steps I made so far:
I tryed to put NotificationPane directly to my scene and call
show() - it works.
Now the Idea is to get the current pane by calling
stage.getScene().getRoot(), wrap it to NotificationPane and then call
show() - it doesn't work and I have no idea why.
((BorderPane) pane).setCenter(new Label("TEST")); this line is replacing buttons with text label, so stage.getScene().getRoot() is returning the right object
I made a simple program to test the behaviour. One button to call NotificationPane.
Any suggestions?
Here is my test program:
package application;
import org.controlsfx.control.NotificationPane;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Button notificationPaneButton = new Button("NotificationPane");
notificationPaneButton.setOnAction(e -> showNotificationPane(primaryStage, "Notification text"));
VBox vbox = new VBox(5);
vbox.setAlignment(Pos.CENTER);
vbox.getChildren().addAll(notificationPaneButton);
BorderPane borderPane = new BorderPane();
borderPane.setCenter(vbox);
primaryStage.setTitle("Notifications test");
primaryStage.setScene(new Scene(borderPane, 300, 200));
primaryStage.show();
}
public void showNotificationPane(Stage stage, String message) {
Parent pane = stage.getScene().getRoot();
// ((BorderPane) pane).setCenter(new Label("TEST"));
NotificationPane notificationPane = new NotificationPane(pane);
notificationPane.setText(message);
if (notificationPane.showingProperty().get()) {
notificationPane.hide();
System.err.println("hide");
} else {
notificationPane.show();
System.err.println("show");
}
}
}
Ok, I see the problem now. Wrapping current pane is not enough, I also have to add the NotificationPane to the scene. Right?
Anyway my current solution is following:
get current scene
get current pane
wrap pane
replace current scene with the new one
To avoid wrapping NotificationPane multiple times I check if current pane is already a NotificationPane and then call show().
public void showNotificationPane(Stage stage) {
Scene scene = stage.getScene();
Parent pane = scene.getRoot();
if (!(pane instanceof NotificationPane)){
NotificationPane notificationPane = new NotificationPane(pane);
scene = new Scene(notificationPane, scene.getWidth(), scene.getHeight());
stage.setScene(scene);
notificationPane.show();
} else {
((NotificationPane)pane).show();
}
}

Multiple default buttons in JavaFX

I have a JavaFX application with 2 tabs in a tabPane. And I would like each tab to have a default button (a button with defaultButton="true"). However, only the button in the first tab reacts on Enter key presses. The button in the second tab ignores Enter key presses.
Hypothesis: Oracle documentation states:
A default Button is the button that receives a keyboard VK_ENTER
press, if no other node in the scene consumes it.
Hence, I guess the problem is that both buttons are in a single scene. Do you know how to get 2 tabs in JavaFX, each with a working default button?
There can only be one default button: you want the button in the currently selected tab to be the default button. Simply add a listener to the selected property of each tab and make the corresponding button the default button, or use a binding to achieve the same:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class MultipleDefaultButtons extends Application {
#Override
public void start(Stage primaryStage) {
TabPane tabPane = new TabPane();
tabPane.getTabs().addAll(createTab("Tab 1"), createTab("Tab 2"));
primaryStage.setScene(new Scene(tabPane, 400, 400));
primaryStage.show();
}
private Tab createTab(String text) {
Tab tab = new Tab(text);
Label label = new Label("This is "+text);
Button ok = new Button("OK");
ok.setOnAction(e -> System.out.println("OK pressed in "+text));
VBox content = new VBox(5, label, ok);
tab.setContent(content);
ok.defaultButtonProperty().bind(tab.selectedProperty());
return tab ;
}
public static void main(String[] args) {
launch(args);
}
}

Is it possible to detect when the mouse cursor is over a hidden stage or scene in JavaFX?

I'm trying to implement a control that exists on a stage that, when moused over will be shown, and then when the mouse leaves it's bounds, hides itself.
I've tried
Stage.getScene().setOnMouseEntered((MouseEvent mEntered) -> {Stage.show();});
Stage.getScene().setOnMouseExited((MouseEvent mExited) -> {Stage.hide();});
I'm not really very surprised it didn't work, but it doesn't help me very much.
Is it possible to detect when the mouse is over the Stage or Scene when it is hidden?
I've read your multiple threads now and didn't get what you really intend to achieve when you put it all together. Maybe if you elaborate on that, another solution may be more appropriate.
Anyway, for your transparent buttons the solution is to use a transparency low enough to let the content below it shine through, but still not totally transparent in order to register mouse events. You wouldn't get mouse events on the application if it were fully transparent.
Here's an example about how you could do it:
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
Pane pane = new Pane();
HBox hbox = new HBox();
hbox.setSpacing(12);
Button button1 = new Button("Button 1");
Button button2 = new Button("OMG Magic!");
Button button3 = new Button("Button 3");
hbox.getChildren().addAll(button1, button2, button3);
pane.getChildren().add( hbox);
final Scene scene = new Scene(pane, Color.TRANSPARENT);
// scene.getRoot().setStyle("-fx-background-color: transparent");
scene.getRoot().setStyle("-fx-background-color: rgba(1,1,1,0.004)");
primaryStage.initStyle(StageStyle.TRANSPARENT);
primaryStage.setScene(scene);
primaryStage.show();
// initial opacity of button 2
button2.setOpacity(0.0);
// button2: fade-in on mouse scene enter
scene.addEventHandler(MouseEvent.MOUSE_ENTERED, evt -> {
System.out.println("Mouse entered");
FadeTransition fadeTransition = new FadeTransition(Duration.millis(300), button2);
fadeTransition.setFromValue(0.0);
fadeTransition.setToValue(1.0);
fadeTransition.play();
});
// button2: fade-out on mouse scene exit
scene.addEventHandler(MouseEvent.MOUSE_EXITED, evt -> {
System.out.println("Mouse exited");
FadeTransition fadeTransition = new FadeTransition(Duration.millis(300), button2);
fadeTransition.setFromValue(1.0);
fadeTransition.setToValue(0.0);
fadeTransition.play();
});
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
It shows 3 buttons. When you enter the button 2 or the pane between buttons 1 and button 3 with the mouse, then button 2 will magically appear :-)

Categories