JavaFX - setStyle() changes not showing - css

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;
}

Related

when i add a button the scene color disappear (javaFx) [duplicate]

Why MenuBar changes the background color of the scene in this code sample? It should be blue but it is white.
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.MenuBar;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
scene.setFill(Color.rgb(0, 0, 255));
primaryStage.setScene(scene);
primaryStage.show();
MenuBar menuBar = new MenuBar();
}
public static void main(String[] args) {
launch(args);
}
}
The white background you are seeing is the background of the BorderPane. This background color is set when the default stylesheet is loaded.
The reason that you only see this when the MenuBar is created is that CSS is only applied (unless you force it) when the first control is created. This is by design, to prevent the overhead of loading stylesheets and applying CSS for applications that don't need them (e.g. for games or simulations that manage all their own graphics). Since all controls are styled by CSS, just instantiating a control forces CSS to be applied.
The fix is to make the background of the BorderPane transparent.
Either
root.setStyle("-fx-background-color: transparent;");
or
root.setBackground(Background.EMPTY);
Of course, since you have to set the background of the root pane, you may as well set that to blue instead of setting the fill of the Scene:
BorderPane root = new BorderPane();
root.setBackground(new Background(new BackgroundFill(Color.BLUE, CornerRadii.EMPTY, Insets.EMPTY)));
Scene scene = new Scene(root,400,400);
Or, you can use an external style sheet:
.root {
-fx-background-color: blue ;
}
Also see this related post and this OTN discussion.

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

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.

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.

Getting a MouseEvent to target ImageViews inside a TilePane

I have many ImageViews inside a TilePane, that is inside a StackPane and then a ScrollPane. There is no border, padding, or margin between the children of the TilePane so there is no chance that I'm not clicking on an ImageView. When I click on an image, I want the target of the MouseEvent to be the ImageViews, but instead it is the TilePane.
How can I get the event chain to end on an ImageView instead of ending early on the TilePane?
Otherwise, is there a way I can get the ImageView using other information? Perhaps using the coordinates of the event?
The usual way I do this is just to register the mouse listener with the node in which I am interested; in your case this means register a mouse listener with each ImageView. It's easy then to have each mouse listener have a reference to the particular image view with which it's registered, or to other data (e.g. a filename) if you need.
One thing that might be happening: if your images have transparent pixels, then mouse clicks on that part of the image will by default "drop through" to the node below. You can change this behavior by calling imageView.setPickOnBounds(true); on the image views.
Some test code. If you run this you'll see some numbered images with different colored backgrounds. About 1 in 4 have transparent backgrounds (they appear white). If you click on these (but not on the actual text of the number), you'll see the mouse handlers registered with the scroll pane and stack pane have the tile pane as the target, and the handler registered with the ImageView is not even invoked. For those without the transparent background, the target is always the ImageView. If you select the check box, so pickOnBounds is true for all the ImageViews, both transparent and opaque images behave as you want.
import java.util.Random;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.TilePane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class ImageViewClickTest extends Application {
private static final Random RNG = new Random();
#Override
public void start(Stage primaryStage) {
TilePane tilePane = new TilePane();
CheckBox pickOnBounds = new CheckBox("Pick on bounds");
pickOnBounds.setPadding(new Insets(16));
for (int i=1; i<=200; i++) {
ImageView imageView = createImageView(i);
imageView.pickOnBoundsProperty().bind(pickOnBounds.selectedProperty());
// mouse handler directly on image view:
// can access image-view specific data...
String message = "Clicked on Image "+i ;
imageView.setOnMouseClicked(e ->
System.out.println("From handler on ImageView: "+message));
tilePane.getChildren().add(imageView);
}
StackPane stack = new StackPane(tilePane);
stack.setOnMouseClicked(e -> {
// source will be the stack pane
// target will be the top-most node
// (i.e. the ImageView, in most cases)
System.out.println("From handler on stack pane: Source: "+e.getSource());
System.out.println("From handler on stack pane: Target: "+e.getTarget());
});
ScrollPane scroller = new ScrollPane(stack);
scroller.setFitToWidth(true);
scroller.setOnMouseClicked(e -> {
// source will be the scroll pane
// target will be the top-most node
// (i.e. the ImageView, in most cases)
System.out.println("From handler on scroller: Source: "+e.getSource());
System.out.println("From handler on scroller: Target: "+e.getTarget());
});
BorderPane root = new BorderPane(scroller, pickOnBounds, null, null, null);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private ImageView createImageView(int index) {
Label label = new Label(Integer.toString(index));
label.setAlignment(Pos.CENTER);
label.setMinSize(48, 48);
label.setStyle(randomStyle());
Image image = new Scene(label, Color.TRANSPARENT).snapshot(null);
ImageView imageView = new ImageView(image);
return imageView ;
}
private String randomStyle() {
StringBuilder style = new StringBuilder();
style.append("-fx-background-color: -fx-background;");
style.append("-fx-background: ");
if (RNG.nextDouble() < 0.25) {
style.append( "transparent;");
style.append(" -fx-text-fill: black;") ;
} else {
String bg = String.format("#%02x%02x%02x;",
RNG.nextInt(256), RNG.nextInt(256), RNG.nextInt(256));
style.append(bg);
}
return style.toString();
}
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