How to affect other elements on hover in JavaFX Scene Builder? - css

I am working on an app in JavaFX using SceneBuilder, and I want to add an CSS code that will display label when I hover on a button, I tried:
.label
{
-fx-text-fill: transparent;
}
.button:hover ~ .label
{
-fx-text-fill: black;
}
All the elements are in the same container.
So my question is how can I affect label using button?

You will not be able to do this purely with JavaFX CSS since the Label is not a descendant of the button.
You can, however, alter the style of the Label by listening to the hoverProperty of your Button and setting the style for the Label appropriately:
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.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Sample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Sample layout
VBox root = new VBox(5);
root.setAlignment(Pos.TOP_CENTER);
root.setPadding(new Insets(5));
// Create the Button and Label
Button button = new Button("Hover Me!");
Label label = new Label("You hovered like a pro!");
// Add a listener to the button's hoverProperty. When it is triggered, we can update the
// styleclass of the label.
button.hoverProperty().addListener((observable, oldValue, newValue) -> {
// If the current state is true, add the button-hovered styleclass
if (newValue) {
label.getStyleClass().add("button-hovered");
} else {
// Otherwise, we remove that class
label.getStyleClass().remove("button-hovered");
}
});
// Add the button and label to the layout
root.getChildren().addAll(button, label);
// Create the scene
Scene scene = new Scene(root);
// Apply CSS
scene.getStylesheets().add("css/style.css");
primaryStage.setScene(scene);
primaryStage.show();
}
}
HOWEVER, in your case, you may be going about it the wrong way since you just want to keep the Label hidden until the user hovers over the Button. In that case, it is probably simpler to just bind the visibleProperty of the Label to your hoverProperty of the Button. Doing so takes just one line of code instead of using the listener:
label.visibleProperty().bind(button.hoverProperty());
Of course, you'll want to remove the .label selector from your CSS if you go this route, since it will still make the text transparent.

Related

Radio Button not showing colored text

I have to use a Radio Button to make three Radio Buttons Red, Blue, and Green. The radio buttons are not changing to those colors. In addition to those changes, the others must stay black font. I commented out the Red setTextFill Font and there was no effect. I also commented the Black fonts and they did not do make an effect. The isSelection works.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class ButtonRadio extends Application {
#Override
public void start(Stage primaryStage) {
// primary stage
primaryStage.setTitle("javaFX");
// label for text
Label labelfirst = new Label("Choose a button");
// vBox for buttons
VBox layout = new VBox(3);
// radio buttons
RadioButton radio1, radio2, radio3;
radio1 = new RadioButton("Red");
radio2 = new RadioButton("Blue");
radio3 = new RadioButton("Green");
// ToggleGroup for entering
ToggleGroup group = new ToggleGroup();
// radio button variables of toggle groups
radio1.setToggleGroup(group);
radio2.setToggleGroup(group);
radio3.setToggleGroup(group);
// if statements for radio buttons and fonts red, blue, green
if (group.getSelectedToggle() != null) {
if (radio1.isSelected()) {
radio1.setTextFill(Color.RED);
radio2.setTextFill(Color.BLACK);
radio3.setTextFill(Color.BLACK);
} else if (radio2.isSelected()) {
radio2.setTextFill(Color.BLUE);
radio1.setTextFill(Color.BLACK);
radio3.setTextFill(Color.BLACK);
}
else if (radio3.isSelected())
{
radio3.setTextFill(Color.GREEN);
radio1.setTextFill(Color.BLACK);
radio2.setTextFill(Color.BLACK);
}
}
// layout to put in parent
layout.getChildren().addAll(labelfirst, radio1, radio2, radio3);
// put in scene and stage to show
Scene scene1 = new Scene(layout, 400, 250);
primaryStage.setScene(scene1);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Edit:
I tried adding a listener. It still does not work.
if (radio1.isSelected()) {
radio1.setOnAction((event) -> {
radio1.setTextFill(Color.RED);
});
Your if statement will only run once, checking the selected status of your RadioButtons only when the application first runs.
You need to listen for changes to the selected RadioButton and act accordinly. This is easily done by adding a listener to the ToggleGroup's selectedToggleProperty().
Remove your if block and replace it with something like this:
group.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
// Set the previously-selected RadioButton text to BLACK
if (oldValue != null) ((RadioButton) oldValue).setTextFill(Color.BLACK);
// Set the color for the newly-selected RadioButton
if (newValue.equals(radio1)) {
((RadioButton) newValue).setTextFill(Color.RED);
} else if (newValue.equals(radio2)) {
((RadioButton) newValue).setTextFill(Color.BLUE);
} else if (newValue.equals(radio3)) {
((RadioButton) newValue).setTextFill(Color.GREEN);
}
});

JavaFx Overlapping mouse events

I’m trying to build a board game interface where the user can switch between multiple eras, each one with its own board. To do so, I’m creating 4 different board, each within its own pane, and I’m toggling the nodes Visibility and disabling the nodes that aren’t being used. The problem I have is the mouse event handlers I’m using to see where the user is clicking only work on the top layer, the last one that was rendered. The event Handlers underneath don’t work even if they are enabled.
Here’s what I wrote:
static EventHandler<MouseEvent> eventMouseClickRoad = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
final Shape innerShape = (Shape) (e.getTarget());
System.out.println("click");
Color color = (Color) innerShape.getFill();
if(color.getOpacity() != 1)
{
innerShape.setFill(Color.RED);
//and do the data treatment
}
}
};
public void boardControler(Vector2DList sideList,PointList hexEdge,Pane groupPane,float scaleX, float scaleY, float buttonSize)
{
//set road button
for(Vector2D v : sideList.getVectorList()){
Path mypath = new Path(new MoveTo(v.getP1().getX(),v.getP1().getY()),new LineTo(v.getP2().getX(),v.getP2().getY()));
groupPane.getChildren().add(mypath);
}
for(Vector2D v : sideList.getVectorList()){
float midX=(v.getP1().getX()+v.getP2().getX())/2;
float diffY=v.getP1().getY()-v.getP2().getY();
float diffX=v.getP1().getX()-v.getP2().getX();
Rectangle rectangle = new Rectangle(midX-buttonSize/2,midY-Math.abs(diffY)+buttonSize+(Math.abs(diffY)-scaleY/4),buttonSize,(scaleY/2)-(buttonSize*2));
rectangle.setRotate(Math.toDegrees(Math.atan(diffY/diffX))+90);
rectangle.setFill(Color.TRANSPARENT);
rectangle.addEventFilter(MouseEvent.MOUSE_ENTERED, Event.eventMouseEntered);
rectangle.addEventFilter(MouseEvent.MOUSE_EXITED, Event.eventMouseExit);
rectangle.addEventFilter(MouseEvent.MOUSE_CLICKED, Event.eventMouseClickRoad);
groupPane.getChildren().add(rectangle);
}
}
And this is what i use to toggle the board that's being used:
to disable
for(Node n : groupPane2.getChildren())
{
n.setDisable(true);
n.setManaged(false);
n.setVisible(false);
}
to enable
for(Node n : groupPane2.getChildren())
{
n.setDisable(false);
n.setManaged(true);
n.setVisible(true);
}
Perhaps using a StackPane would be the solution here. Your question doesn't include much code to show all of your context, but the MCVE below may help to demonstrate the idea.
Basically, we create a StackPane as our root display container for all of your boards. Your "boards" can be anything, a Pane, another StackPane, or a VBox like in my example. This should allow you to continue using whatever layout system you currently are.
One thing to note, it appears that each board will need to have a background set, or the lower boards will show through and may accept mouse events.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class StackPaneSample extends Application {
public static void main(String[] args) {
launch(args);
}
private static StackPane stackPane = new StackPane();
#Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// Create our StackPane
stackPane.setStyle("-fx-border-color: black");
VBox.setVgrow(stackPane, Priority.ALWAYS);
// Let's create 3 "boards" for our StackPane. A background color seems necessary to hide layers below the top one
VBox board1 = new VBox() {{
setStyle("-fx-background-color: whitesmoke");
setAlignment(Pos.CENTER);
setUserData("Board #1");
getChildren().add(new Label((String) getUserData()));
}};
VBox board2 = new VBox() {{
setStyle("-fx-background-color: whitesmoke");
setAlignment(Pos.CENTER);
setUserData("Board #2");
getChildren().add(new Label((String) getUserData()));
}};
VBox board3 = new VBox() {{
setStyle("-fx-background-color: whitesmoke");
setAlignment(Pos.CENTER);
setUserData("Board #3");
getChildren().add(new Label((String) getUserData()));
}};
stackPane.getChildren().add(board1);
stackPane.getChildren().add(board2);
stackPane.getChildren().add(board3);
// Create three buttons that will switch between the boards
Button btnBoard1 = new Button("Board #1");
Button btnBoard2 = new Button("Board #2");
Button btnBoard3 = new Button("Board #3");
HBox hbButtons = new HBox(20) {{
setAlignment(Pos.CENTER);
setPadding(new Insets(5));
getChildren().addAll(btnBoard1, btnBoard2, btnBoard3);
}};
// Finish out layout
root.getChildren().addAll(
stackPane,
new Separator(Orientation.HORIZONTAL),
hbButtons
);
// ** Now let's add our functionality **
// Print out which board has been clicked upon
// We need to first cast our List to VBox
for (Node vbox : stackPane.getChildren()) {
vbox.setOnMouseClicked(event -> System.out.println("Clicked on " + vbox.getUserData()));
}
// Set the buttons to set the top board
btnBoard1.setOnAction(event -> selectBoard(board1));
btnBoard2.setOnAction(event -> selectBoard(board2));
btnBoard3.setOnAction(event -> selectBoard(board3));
// Show the Stage
primaryStage.setWidth(400);
primaryStage.setHeight(300);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
// Method to remove the board and readd it, placing it on top of all others.
private static void selectBoard(VBox board) {
stackPane.getChildren().remove(board);
stackPane.getChildren().add(board);
}
}
The Result:
I am, admittedly, not familiar with the Cartesian coordinates you mention in your comment, so perhaps this won't work for you. Adding more code/context to your question might help us narrow down the issue better.

JavaFX - setStyle() changes not showing

I am creating a JavaFX application and I am having problems changing the background colors for certain components. For the buttons I am able to change their background radius, but not their background color. For the TableView I am unable to change the background color as well.
Here is my code and a picture of what I am seeing.
import javafx.geometry.Pos;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableView;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class HomeUI extends Application {
private TableView transactionTable = new TableView();
private Button importButton = new Button("Import");
private Button trendButton = new Button("Trends");
private Button transactionButton = new Button("Transactions");
public static void main(String[] args){
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
// Set the text of defined fields
primaryStage.setTitle(" Budget Tracker");
// Import button information
// Create Anchor pane
AnchorPane anchorPane = new AnchorPane();
anchorPane.setPrefHeight(668.0);
anchorPane.setPrefWidth(1112.0);
anchorPane.setStyle("-fx-background-color: #545e75;");
// VBox to hold all buttons
VBox vBox = new VBox();
vBox.setPrefWidth(195);
vBox.setPrefHeight(668);
vBox.prefHeight(668);
vBox.prefWidth(203);
vBox.setStyle("-fx-background-color: #82a0bc;");
vBox.setLayoutX(0);
vBox.setLayoutY(0);
vBox.setAlignment(Pos.CENTER);
// importButton settings
importButton.setMnemonicParsing(false);
importButton.setPrefWidth(300);
importButton.setPrefHeight(80);
importButton.setStyle("-fx-background-color: #cacC9cc");
importButton.setStyle("-fx-background-radius: 0;");
// trendButton settings
trendButton.setPrefWidth(300);
trendButton.setPrefHeight(80);
trendButton.setStyle("-fx-background: #bcbdc1");
trendButton.setStyle("-fx-background-radius: 0");
// transactionButton settings
transactionButton.setPrefWidth(300);
transactionButton.setPrefHeight(80);
transactionButton.setStyle("-fx-base: #aeacb0");
transactionButton.setStyle("-fx-background-radius: 0");
// Add buttons to the vBox
vBox.getChildren().addAll(importButton, trendButton, transactionButton);
// TableView settings
transactionTable.setPrefHeight(568);
transactionTable.setPrefWidth(694);
transactionTable.setLayoutX(247);
transactionTable.setLayoutY(50);
transactionTable.setStyle("-fx-background-color: CAC9CC;");
transactionTable.setEditable(false);
// Add components to anchorPane
anchorPane.getChildren().addAll(vBox, transactionTable);
// Add anchorPane to scene and show it
primaryStage.setScene(new Scene(anchorPane));
primaryStage.show();
}
}
Buttons
By setting the style property, you replace the old style. Doing this multiple times does not combine the styles. You should set a value that combines the rules.
Instead of
transactionButton.setStyle("-fx-base: #aeacb0");
transactionButton.setStyle("-fx-background-radius: 0");
use
transactionButton.setStyle("-fx-base: #aeacb0; -fx-background-radius: 0;");
TableView
TableView shows little of it's own background. Most coloring you'll see is the background color of the TableRows that are added as descendants of the TableView. You'll need to use a CSS stylesheet to do this though (unless you want to use a rowFactory to do the styling).
.table-view .table-row-cell {
-fx-background-color: #CAC9CC;
}

prompt text in text field javaFX

From JavaFX CSS I want to apply an effect only to the prompt-text without affecting the text in a TextField but do not know how to access that item. I can only change the color with -fx-prompt-text-fill. When I apply an effect to the text the prompt-text it is also affected, why?
.text-field {
-fx-prompt-text-fill: gray;
}
.text-field > .text {
-fx-effect: dropshadow( two-pass-box , blue , .5, 10 , 1 , 1);
}
In the above code also applies shadow to prompt-text what I want to avoid !!
You can style the Prompt text with a stylesheet if you use JFoenix. The CSS-Class is .jfx-text-field and the property -fx-prompt-text-fill.
Example:
.jfx-text-field {
-fx-prompt-text-fill: #989898;
}
If you need other components, look it up: JFoenix GitHub components page
The prompt text only shows when the text field is empty, so the easiest way I can see to do this is to define and "empty" CSS PseudoClass. Set the effect on the text as you want it, and then define the effect for text in an empty text field to be null:
.text-field {
-fx-prompt-text-fill: gray;
}
.text-field .text {
-fx-effect: dropshadow( two-pass-box , blue , .5, 10 , 1 , 1);
}
.text-field:empty .text {
-fx-effect: null ;
}
To make the pseudoclass work, you need to register a listener with the text property in the text field and update it:
TextField textField = new TextField();
textField.setPromptText("Enter text");
PseudoClass empty = PseudoClass.getPseudoClass("empty");
textField.pseudoClassStateChanged(empty, true);
textField.textProperty().addListener((obs, oldText, newText) -> {
textField.pseudoClassStateChanged(empty, newText.isEmpty());
});
Here is a SSCCE (with the CSS code above in prompt-text-styling.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.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TextFieldPromptStylingTest extends Application {
#Override
public void start(Stage primaryStage) {
TextField textField = new TextField();
textField.setPromptText("Enter text");
PseudoClass empty = PseudoClass.getPseudoClass("empty");
textField.pseudoClassStateChanged(empty, true);
textField.textProperty().addListener((obs, oldText, newText) -> {
textField.pseudoClassStateChanged(empty, newText.isEmpty());
});
Button okButton = new Button("OK");
VBox root = new VBox(10, textField, okButton);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(24));
Scene scene = new Scene(root);
scene.getStylesheets().add("prompt-text-styling.css");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX ContextMenu doesn't auto-hide

I have a JavaFX ContextMenu assigned to the right mouse button click of a scrollpane. It opens, but it doesn't close when you click outside the scrollpane. I could add another mouse event to the scrollpane in order to hide it, but that solves only 1 problem. The main problem is that when I click on any component of the scrollpane, then the context menu remains open.
Example: Open popup via right mouse button click, then click on the button. The popup menu is still open.
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
final ContextMenu contextMenu = new ContextMenu();
MenuItem item1 = new MenuItem("About");
MenuItem item2 = new MenuItem("Preferences");
contextMenu.getItems().addAll(item1, item2);
Rectangle rect = new Rectangle( 100,100,150,150);
Button button = new Button( "Button Text");
// create nodes
Group root = new Group();
root.getChildren().add( rect);
root.getChildren().add( button);
// create scrollpane
ScrollPane sp = new ScrollPane( root);
sp.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.isSecondaryButtonDown()) {
contextMenu.show( sp, event.getScreenX(), event.getScreenY());
}
}
});
// create scene
Scene scene = new Scene(sp, 400, 400, Color.WHITE);
// add scene to primary stage
primaryStage.setScene( scene);
primaryStage.show();
}
}
The documentation says that there's a setAutoHide method, but it doesn't work in my case:
Specifies whether Popups should auto hide. If a popup loses focus and
autoHide is true, then the popup will be hidden automatically. The
only exception is when owner Node is specified using
show(javafx.scene.Node, double, double). Focusing owner Node will not
hide the PopupWindow.
#defaultValue false
Thank you very much!
Interacting with child elements of the parent, will get a focus to that parent. So the context menu will not hide when the button in your code is clicked.
Try these two approaches:
1) Manually manage the visibility of context menu, i.e. hide it on button click:
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
contextMenu.hide();
}
});
2) Use setContextMenu() instead of showing the context menu on mouse press event:
sp.setContextMenu(contextMenu);
I know that this is old post, but for any newcomer I found a new solution. I have an jdk 1.8 and I have the same problem as you, but I have a dynamic generated context menu in TableView. So when you right click on the row I need another context menu by the row content. The key for my solution is that you execute show method in the context menu you pass on the window parameter to the method. Example of my code is below:
ContextMenu contextMenu = this.createContextMenu();
contextMenu.show(this.tableView.getScene().getWindow(), mouseEvent.getScreenX(), mouseEvent.getScreenY());
And when I click to another location of my program, the context menu hide.

Resources