How can I create a popover like in the DatePicker class from javafx.scene.control.DatePicker as seen here:
The popover should when displayed be on top of all the other components as seen here (the popover is above the TextField):
Found a pretty simple solution to my problem, here is a code snippet in case people encounter the same problem
package main;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.Label;
import javafx.scene.control.MenuButton;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
BorderPane rootPane = new BorderPane();
MenuButton openButton = new MenuButton("Open Context Menu");
BorderPane contentPane = new BorderPane();
CustomMenuItem item = new CustomMenuItem(contentPane);
openButton.setStyle("-fx-selection-bar: transparent;"); //this is optional. it makes the blue background that appears when something is focused transparent
contentPane.setPrefSize(300, 300);
Label text = new Label("The ContextMenu will only close when you click the\nbutton below OR click outside of the ContextMenu.\nHow neat is that?");
text.setStyle(" -fx-text-fill: -fx-text-base-color;"); //needs to bet set if you want the selection-bar to be transparent. if not set the text will become invisible
contentPane.setTop(text);
Button closeButton = new Button("Close this popover");
closeButton.setOnAction(x -> {
openButton.hide();
});
contentPane.setBottom(closeButton);
item.setHideOnClick(false); // this will stop the ContextMenu from being hidden when clicking inside of it.
openButton.getItems().add(item);
rootPane.setCenter(openButton);
Scene scene = new Scene(rootPane, 550, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
}
I just placed a Pane with all of my content inside of a CustomMenuItem and added that CustomMenuItem to my MenuButton.
Related
I have a project from uni where I have to make an application with Java (in a model view controller format), and I want to make tabs in my application, but it doesn't seem to be working.
I looked up a lot of tutorials, and they all tell me the same way of how to use TabPane, but it doesn't work for me.
Here is the code I have in my Application Loader class:
package main;
import controller.ModuleChooserController;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.stage.Stage;
import model.StudentProfile;
import view.ModuleChooserRootPane;
public class ApplicationLoader extends Application {
private ModuleChooserRootPane view;
#Override
public void init() {
//create model and view and pass their references to the controller
StudentProfile model = new StudentProfile();
view = new ModuleChooserRootPane();
new ModuleChooserController(view, model);
}
#Override
public void start(Stage stage) throws Exception {
//whilst you can set a min width and height (example shown below) for the stage window,
//you should not set a max width or height and the application should
//be able to be maximised to fill the screen and ideally behave sensibly when resized
stage.setMinWidth(530);
stage.setMinHeight(500);
TabPane tabPane = new TabPane();
Tab tab = new Tab("Testing");
tabPane.getTabs().add(tab);
stage.setTitle("Final Year Module Chooser Tool");
stage.setScene(new Scene(view));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I have the TabPane implemented, but nothing comes up. I also tried implementing the TabPane in my "view" package, but I had no luck there either.
Here is the code for the ModuleRootChooserPane:
package view;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
//You may change this class to extend another type if you wish
public class ModuleChooserRootPane extends BorderPane {
private ButtonPane bp;
private ProfileCreator profileCreator;
public ModuleChooserRootPane() {
//This sets the colour of background
this.setStyle("-fx-background-color: #EDF1F3;");
//Creates a new instance of the buttonPane (Used from ButtonPane.java) and ProfileCreator
bp = new ButtonPane();
profileCreator = new ProfileCreator();
//This adds the padding on the left so that "submit" button is in line with text fields
bp.setPadding(new Insets(0, 0, 0, 120));
//Creates a new VBox which adds the ProfileCreator and the button pane
VBox rootContainer = new VBox(profileCreator, bp);
rootContainer.setPadding(new Insets(100,100,100,100));
this.getChildren().add(rootContainer);
}
}
You choose to work without fxml file, so you need to create your view into the class ModuleChooserRootPane, every graphic element have to be here, or in other classes used here.
So you have to add your TabPane in its constructor too :
public ModuleChooserRootPane() {
...
//this.getChildren().add(rootContainer);
setLeft(rootContainer); // or Top/Bottom/Right/Center
TabPane tabPane = new TabPane();
Tab tab = new Tab("Testing");
tabPane.getTabs().add(tab);
setCenter(tabPane); // or Top/Bottom/Right/Left
}
A BorderPane is good idea for root element because it's has several zones to add element, but fo sor you need to use setLeft(), setRight(), setCenter(), setTop() and setBottom() rather than just getChildren().add() where you cannot control the place
Example for adding content in the different tabs :
TabPane tabPane = new TabPane();
Tab tab = new Tab("Testing");
tab.setContent(new VBox(new Label("Here is the testing place"), new Circle(15, 12, 10)));
Tab tab2 = new Tab("Testing2");
HBox hboxContentTab2 = new HBox();
hboxContentTab2.getChildren().add(new Ellipse(10, 10, 10, 13));
hboxContentTab2.getChildren().add(new Label("Here is the BIS testing place"));
tab2.setContent(hboxContentTab2); // add a Node created before, ot can be whatever you wan, borderpane, gridpane, hbox, vbox, label ...
tabPane.getTabs().addAll(tab, tab2);
As stated in the title, I have fxml files, I have a UI that is set up with three labels/buttons up top and the lower half of the window has a pane. Every time a label/button is clicked, the pane must switch to that corresponding fxml file. So in other words, the pane must always be in the same position, kind of like a tabbed layout but without tabs.
I know I can achieve this with just loading a new instance of an fxml file but, I want to avoid that because when a user click on a tab he previously was on, he should be able to see his earlier input.
I have some main.java that starts the program. Some controller.java that controls the UI when it is first loaded, and some fxml file corresponding to that initial view. How can I go about implementing this transition functionality? P.S. I am very novice at JavaFX.
Here is a MCVE of how you can achieve it.
It can of course be implemented using FXML :
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class StageTest extends Application{
private Pane pane1, pane2, mainPane;
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("Switch Panes");
Button button1 = new Button("Show Pane 1");
button1.setOnAction(e -> showPane1());
Button button2 = new Button("Show Pane 2");
button2.setOnAction(e -> showPane2());
HBox buttonsPane = new HBox(5.);
buttonsPane.getChildren().addAll(button1, button2);
pane1 = getPane("PANE ONE");
pane2 = getPane("PANE TWO");
mainPane = new StackPane(pane1);
BorderPane root = new BorderPane();
root.setTop(buttonsPane);
root.setCenter(mainPane);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
private void showPane1() {
mainPane.getChildren().clear();
mainPane.getChildren().add(pane1);
}
private void showPane2() {
mainPane.getChildren().clear();
mainPane.getChildren().add(pane2);
}
private Pane getPane(String txt) {
VBox pane = new VBox();
pane.getChildren().addAll(new TextArea(txt+" add text here: "));
return pane;
}
public static void main(String[] args) {
launch(args);
}
}
I'm making a simple text editor for training purposes. I want to create a button which would center my text input from TextArea just like in MS Word. I have my button in FXML, but I don't know what method should I use for my TextArea object, I tried setStyle() or getChild() but neither worked.
<Button onAction="#toTheCenter" text="center"/>
Thats my button in FXML
<center>
<TextArea fx:id="textArea"/>
</center>
Thats TextArea
#FXML
private void toTheCenter(ActionEvent event){
String text = textArea.getText();
}
And thats the method from controller.
You need to set the -fx-text-alignment property on the text node of the text area.
The best way to do this dynamically is to define a custom CSS pseudoclass for the text area:
PseudoClass centered = PseudoClass.getPseudoClass("centered");
and then in your external CSS file you can do
.text-area:centered .text {
-fx-text-alignment: center ;
}
Then you can call
textArea.pseudoClassStateChanged(centered, true);
to switch centering on, and
textArea.pseudoClassStateChanged(centered, false);
to switch it off.
Here is a SSCCE (put the CSS code above in centering-text-area.css):
import javafx.application.Application;
import javafx.css.PseudoClass;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TextAreaWithCentering extends Application {
#Override
public void start(Stage primaryStage) {
TextArea textArea = new TextArea();
PseudoClass centered = PseudoClass.getPseudoClass("centered");
ToggleButton center = new ToggleButton("Center");
center.selectedProperty().addListener((obs, wasCentered, isNowCentered) ->
textArea.pseudoClassStateChanged(centered, isNowCentered));
BorderPane.setAlignment(center, Pos.CENTER);
BorderPane.setMargin(center, new Insets(5));
BorderPane root = new BorderPane(textArea, null, null, center, null);
Scene scene = new Scene(root, 600, 600);
scene.getStylesheets().add("centering-text-area.css");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
#text-area *.text { -fx-text-alignment: center; }
Create a style.css file in your project folder and then add the following line to the java code.
scene.getStylesheets().addAll(this.getClass().getResource("style.css").toExternalForm()); primaryStage.setScene(scene);
I would like to execute multiple stage operations in one frame :
stage.sizeToScene()
stage.centerOnScreen()
Currently I can see that the stage is first resized, then centered. I would like both operations to be done atomically on the same re-paint.
Here is a working example :
package sample;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Main extends Application {
private HBox first = new HBox();
private HBox second = new HBox();
private Button change1 = new Button("Go to 2nd");
private Button change2 = new Button("Go to 1st");
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Hello World");
first.setSpacing(10);
first.setAlignment(Pos.CENTER_LEFT);
first.getChildren().addAll(
change1, new Label("Hello"), new Label("World")
);
second.setSpacing(10);
second.setAlignment(Pos.CENTER_LEFT);
second.getChildren().addAll(
change2, new Label("BYE MY FRIENDS, THIS IS MUCH LONGER!")
);
change1.setOnAction(event -> {
primaryStage.getScene().setRoot(second);
primaryStage.sizeToScene();
primaryStage.centerOnScreen();
});
change2.setOnAction(event -> {
primaryStage.getScene().setRoot(first);
primaryStage.sizeToScene();
primaryStage.centerOnScreen();
});
primaryStage.setScene(new Scene(first));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
To reproduce, launch the application, move the window top-left and make it bigger. Then click on the button. You will see that the window is first resized, then moved at the center.
On this example case it's really really fast, because the application is really light. But with a real-world application it's much more noticable.
Is there any way to add a changelistener to group of nodes for following changes?
For example, we can add a changelistener to a tabpane for getting tabselectedproperty.
I want to add changelistener a to a group of buttons for getting buttonActionedProperty! I want to get old button and new button....
Is there any way to do this?
When you compare the tabs in a tab pane to a collection of buttons, you're not really comparing like to like. A tab pane naturally has a sense of which tab is currently selected; buttons just generate events when they are pressed.
If you want your buttons to have a "selected" state, and want a collection of those grouped together so that only one is selected, then consider using ToggleButtons instead. You can put the toggle buttons into a ToggleGroup and register a listener with the toggle group's selectedToggle property:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class ToggleButtonDemo extends Application {
#Override
public void start(Stage primaryStage) {
ToggleButton apples = new ToggleButton("Apples");
ToggleButton oranges = new ToggleButton("Oranges");
ToggleButton pears = new ToggleButton("Pears");
ToggleGroup fruitToggleGroup = new ToggleGroup();
fruitToggleGroup.getToggles().addAll(apples, oranges, pears);
fruitToggleGroup.selectedToggleProperty().addListener((obs, oldToggle, newToggle) ->
System.out.println("Selected toggle changed from "+oldToggle+" to "+newToggle));
HBox root = new HBox(5, apples, oranges, pears);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 350, 75);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If you really just want buttons, and don't have the notion of one of them being selected (I find it hard to see a use case for this), you can just create an ObjectProperty<Button> to store the last button on which an action occurred. Register an event listener with each button to update the property:
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class LastActionTrackingDemo extends Application {
#Override
public void start(Stage primaryStage) {
Button apples = new Button("Apples");
Button oranges = new Button("Oranges");
Button pears = new Button("Pears");
ObjectProperty<Button> lastActionedButton = new SimpleObjectProperty<>();
EventHandler<ActionEvent> buttonActionHandler = event ->
lastActionedButton.set((Button) event.getSource());
apples.addEventHandler(ActionEvent.ACTION, buttonActionHandler);
oranges.addEventHandler(ActionEvent.ACTION, buttonActionHandler);
pears.addEventHandler(ActionEvent.ACTION, buttonActionHandler);
lastActionedButton.addListener((obs, oldButton, newButton) ->
System.out.println("Button changed from "+oldButton+" to "+newButton));
HBox root = new HBox(5, apples, oranges, pears);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 350, 75);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Note there is a subtle different between the appearance of the two demos. The first (with toggle buttons) has a visual representation of which button is selected. The second does not. In both cases you can still set action listeners on the buttons if you need that functionality. There is also a (less subtle) difference in behavior: the toggle buttons can be "unselected"; so if you press the same toggle button twice, the selection goes back to null. This doesn't happen with the buttons.