I want to toggle off sliders when a radiobutton is pressed, and toggle them on when the other is pressed:
Anyways, when I click on toggle off I want the label, sliders and textfields to not be able to be selected. and when you click on Toggle on you can select the sliders etc again.
I get that I need to use ToggleGroup, but not sure how I would go about toggling off the sliders.
I'd probably use a single CheckBox for this UI rather than multiple radio buttons, then you can just bind your slider pane's disable property to the selected property of the CheckBox, but I'll just give an answer here for the UI you have displayed.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ToggleSetup extends Application {
#Override
public void start(Stage stage) throws Exception {
RadioButton on = new RadioButton("on");
RadioButton off = new RadioButton("off");
ToggleGroup toggleState = new ToggleGroup();
on.setToggleGroup(toggleState);
off.setToggleGroup(toggleState);
toggleState.selectToggle(on);
VBox sliderPane = new VBox(
10,
new Slider(),
new Slider(),
new Slider()
);
sliderPane.disableProperty().bind(
Bindings.equal(off, toggleState.selectedToggleProperty())
);
VBox layout = new VBox(10, on, off, sliderPane);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) { launch(args); }
}
Related
Using JavaFX 8, I would like to have a responsive layout with three radio buttons that changes from a horizontal layout to a vertical layout as soon as there is not enough horizontal space to put all radio buttons next to each other.
This means the default layout should be like this:
(x) Radio button 1 ( ) Radio button 2 ( ) Radio button 3
And as soon as there is not enough horizontal space to put all three buttons on a single line, I want the layout to change to this:
(x) Radio button 1
( ) Radio button 2
( ) Radio button 3
Note that I want to avoid intermediate states like:
(x) Radio button 1 ( ) Radio button 2
( ) Radio button 3
I have tried to achieve the desired behaviour with a flow pane that changes its orientation dynamically but I encountered small layout glitches when there was another control (e.g. a text area) positioned underneath the flow pane. Here's the code I have used:
import javafx.application.Application;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextArea;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ResponsiveLayout extends Application {
RadioButton radioButton1 = new RadioButton("Radio button 1");
RadioButton radioButton2 = new RadioButton("Radio button 2");
RadioButton radioButton3 = new RadioButton("Radio button 3");
FlowPane flowPane = new FlowPane(radioButton1, radioButton2, radioButton3);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
flowPane.setHgap(10);
flowPane.setPrefWrapLength(60);
flowPane.widthProperty().addListener((obs, oldWidth, newWidth) -> updateOrientation(newWidth));
VBox container = new VBox(flowPane, new TextArea());
Scene scene = new Scene(container, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private void updateOrientation(Number paneWidth) {
double childrenPadding = flowPane.getHgap() * (flowPane.getChildren().size() - 1);
int extraPadding = 5;
double childrenWidth = radioButton1.getWidth() +
radioButton2.getWidth() +
radioButton3.getWidth() +
childrenPadding +
extraPadding;
if (paneWidth.doubleValue() < childrenWidth) {
flowPane.setOrientation(Orientation.VERTICAL);
} else {
flowPane.setOrientation(Orientation.HORIZONTAL);
}
}
}
When I run this application and carefully change the width of the scene, I can see the following layout glitch right after the orientation of the flow pane changed from vertical to horizontal:
As you can see, there is an unwanted empty space between the radio buttons and the text area. It vanishes when I increase the scene width further or if the window loses focus. In a bigger application with more controls and containers around the flow pane, I can see even more layout glitches so I'm wondering if there is a better approach to achieving the responsiveness I need.
What could I do to improve the flow pane behavior? Or is there a more suitable layout pane that I could use for this?
I'd appreciate any support. Thanks.
You could toggle between VBox and HBox. See if this meets your requirements.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Control;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ResponsiveLayout extends Application
{
RadioButton radioButton1 = new RadioButton("Radio button 1");
RadioButton radioButton2 = new RadioButton("Radio button 2");
RadioButton radioButton3 = new RadioButton("Radio button 3");
HBox hBox = new HBox(radioButton1, radioButton2, radioButton3);
StackPane stackPane = new StackPane(hBox);
VBox vBox = new VBox();
boolean isHBox = true;
double controlWidth = -1;
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage primaryStage)
{
hBox.setMaxSize(Control.USE_PREF_SIZE, Control.USE_PREF_SIZE);
stackPane.setAlignment(Pos.CENTER_LEFT);
VBox container = new VBox(stackPane, new TextArea());
Scene scene = new Scene(container, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
container.widthProperty().addListener((obs, oldWidth, newWidth) -> updateOrientation(newWidth));
}
private void updateOrientation(Number paneWidth)
{
if (isHBox) {
if (hBox.getWidth() == stackPane.getWidth()) {
controlWidth = hBox.getWidth();
vBox.getChildren().addAll(hBox.getChildren());
stackPane.getChildren().clear();
stackPane.getChildren().add(vBox);
isHBox = false;
}
}
else {
if (controlWidth <= stackPane.getWidth()) {
hBox.getChildren().addAll(vBox.getChildren());
stackPane.getChildren().clear();
stackPane.getChildren().add(hBox);
isHBox = true;
}
}
}
}
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.
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 am working on a project using JavaFX. I have included FontAwesome in my project to avoid using images for simple icons. I created the following function in a constants class that generates an HBox with an icon and text that will be called in the setGraphic(Node node). The function is as follows:
public static HBox iconText(String icon, String text) {
return ConstantsClass.iconText(icon, text, 5);
}
public static HBox iconText(String icon, String text, int spacing) {
HBox box = new HBox(spacing);
Label iconLabel = new Label(icon);
iconLabel.setFont(ConstantsClass.fontAwesome);
Label textLabel = new Label(text);
box.getChildren().addAll(iconLabel, textLabel);
return box;
}
The method works perfectly on buttons, such as having a back button with an arrow icon. However it does not seem to work on MenuItems.
I have a menu bar at the top of my application with Menus in them, and MenuItems in those. I tried the same process with a "settings" MenuItem, but the text does not appear unless the cursor is over the item.
MenuItem settings = new MenuItem();
settings.setGraphic(ConstantsClass.iconText(FontAwesome.COG, "Settings")); //Obscuring name of Constants Class
This code has the following results:
When the user just clicks on the menu drop down
When the user hovers over the Menu Item
How can I make the MenuItem always show the icon and text?
It's kind of weird it looks like a bug. If there is no label in the graphic, the graphic seems to display OK (for instance a rectangle seems to work correctly as a graphic). My guess is it is some kind of mess-up in the interaction between the CSS styling rules and the menu skin implementation.
A workaround is to use a snapshot, but that somehow makes the thing being snapshot slightly bold in appearance.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class MenuDisplay extends Application {
public void start(Stage stage) throws Exception {
Label label = new Label("(*)");
label.setStyle("-fx-background-color: null;");
Scene dummyScene = new Scene(label, Color.TRANSPARENT);
SnapshotParameters params = new SnapshotParameters();
params.setFill(Color.TRANSPARENT);
Image snapshot = label.snapshot(params, null);
ImageView imageView = new ImageView(snapshot);
Menu menu = new Menu("Choices");
menu.getItems().addAll(
new MenuItem("Broken Label Graphic", new Label("(*)")),
new MenuItem("OK Rect", new Rectangle(16, 16, Color.FORESTGREEN)),
new MenuItem("Fixed Snapshot", imageView)
);
MenuBar menuBar = new MenuBar(menu);
Scene scene = new Scene(
new VBox(menuBar), 100, 100
);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Maybe somebody else can come up with a better workaround (or fix).
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.