JavaFX Buttons grow with one pixel after their neighbor is pressed - java

I'm making a GUI in JavaFX, and I've stripped the window of the decorations that Windows did and I have made my own controls for close, minimize, and maximize.
The controls work great, but it seems that when I press one of them, it's neighbors change vertical size, increasing it by one or two pixels, as you can see in this image.
Why is this happening?
Im also attaching the code I have so far in the start() method.
public void start(Stage primaryStage) throws Exception {
window = primaryStage;
window.setTitle("Menu Example");
MenuBar file = new MenuBar();
file.setId("file");
Menu fileMenu = new Menu("File");
fileMenu.getItems().addAll(
new MenuItem("New File..."),
new MenuItem("Open file..."),
new MenuItem("Save file"));
fileMenu.setId("#fileMenu");
Menu editMenu = new Menu("Edit");
editMenu.getItems().addAll(
new MenuItem("Undo"),
new MenuItem("Redo"),
new MenuItem("Cut"),
new MenuItem("Copy"),
new MenuItem("Paste")
);
Button closeButton = new Button("X");
closeButton.setId("closeButton");
closeButton.setOnAction(event -> {
window.close();
});
Button minimizeButton = new Button("_");
minimizeButton.setId("minimizeButton");
minimizeButton.setOnAction(event -> {
window.setIconified(true);
});
Button maximizeButton = new Button("?");
maximizeButton.setId("maximizeButton");
maximizeButton.setOnAction(event -> {
if(!window.isMaximized())
window.setMaximized(true);
else
window.setMaximized(false);
});
file.getMenus().addAll(
fileMenu,
editMenu
);
HBox.setHgrow(file, Priority.ALWAYS);
HBox.setHgrow(closeButton, Priority.NEVER);
HBox.setHgrow(minimizeButton, Priority.NEVER);
HBox.setHgrow(maximizeButton, Priority.NEVER);
VBox.setVgrow(closeButton, Priority.NEVER);
hBox = new HBox();
buttonBox = new HBox();
buttonBox.getChildren().addAll(minimizeButton, maximizeButton, closeButton);
hBox.getChildren().addAll(file, buttonBox);
layout = new BorderPane();
layout.setTop(hBox);
Scene scene = new Scene(layout, 400, 400);
scene.getStylesheets().add("Viper.css");
window.setScene(scene);
// window.setMaximized(true);
window.initStyle(StageStyle.UNDECORATED);
window.show();
}
This is my stylesheet:
.root{
-fx-background-color: #2D2E32;
-fx-font-size: 11px;
}
#file{
-fx-background-color: #3E3F43;
}
#file .label{
-fx-text-fill: #EAEAEA;
}
.context-menu{
-fx-background-color: #3E3F43;
}
#closeButton, #minimizeButton, #maximizeButton{
-fx-background-radius: 0;
-fx-background-color: #3E3F43;
-fx-text-fill: #ffffff;
-fx-font-weight: bold;
}
#closeButton:hover{
-fx-background-color: #E46458;
}
#minimizeButton:hover{
-fx-background-color: #80B1E0;
}
#maximizeButton:hover{
-fx-background-color: #80E089;
}

Based on your CSS stylesheet, adding the following selectors will solve problem :
#closeButton:armed, #closeButton:focused,
#minimizeButton:armed, #minimizeButton:focused,
#maximizeButton:armed, #maximizeButton:focused {
-fx-background-insets: 0 0 -1 0, 0, 1, 2;
}
Reason:
The default styleheet modena.css defines the background insets of a Button like -fx-background-insets: 0 0 -1 0, 0, 1, 2;, but if the Button is in one of the pseudo-states of focused or armed, the insets are modified as -fx-background-insets: -0.2, 1, 2, -1.4, 2.6;.
So actually not the vertical size of the neighbours will be increased but the "vertical size" of the armed or focused Button will be decreased.
This is what the selectors above prevent.
Note: If you create a new CSS class like
.windowbutton:armed, .windowbutton:focused {
-fx-background-insets: 0 0 -1 0, 0, 1, 2;
}
then in the code you assign this class to each Button:
closeButton.getStyleClass().add("windowbutton"); // Similar to other buttons
the CSS structure will be a little but more clean.

Related

Add a top menu to JavaFX centered Grid Pane

I'm very new to JavaFX. I am using a grid pane to keep my items centered on the page regardless of how the window is resized. I want to add a menu that runs along the top. I quickly found out grid.setTop(menuBar) is not a member function of grid pane. Is there a way to this?
Can I create two different types of panes in one scene? IE, a GridPane to center items and a BorderPane to get the menu up top? Or should I use CSS styling to get the menubar at the top?
Here is some code I'm using:
public void start(Stage primaryStage) {
try {
primaryStage.setTitle("Bapu Inventory");
BorderPane root = new BorderPane();
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25, 25, 25, 25));
Text scenetitle = new Text("Welcome");
grid.add(scenetitle, 0, 0, 1, 1);
MenuBar menuBar = new MenuBar();
menuBar.prefWidthProperty().bind(primaryStage.widthProperty());
//This is the line I can't figure out. How do I get this to position at top left?
grid.setTop(menuBar);
Any help would be greatly appreciated here. I looked through the documentation Oracle provides but didn't find this feature listed anywhere.
Check if this is what you want:
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
//Create your menu
final Menu menu1 = new Menu("File");
final Menu menu2 = new Menu("Options");
final Menu menu3 = new Menu("Help");
MenuBar menuBar = new MenuBar();
menuBar.getMenus().addAll(menu1, menu2, menu3);
//Your GridPane
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25, 25, 25, 25));
Text scenetitle = new Text("Welcome");
grid.add(scenetitle, 0, 0, 1, 1);
//Add them to root (BorderPane)
root.setTop(menuBar);
root.setCenter(grid);
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}

JavaFx: Space between left, center and right in BorderPane

I have a BorderPane with Panes for left, right and center elements and HBox for top element. A size of my scene is 700 for width and 600 for height. For the left Pane I setMaxWidth at (700 - 400)/2 = 150, the same goes for the right Pane, while the center Pane has max width equal to 400.
public class Main extends Application implements EventHandler<ActionEvent> {
private static BorderPane gamePane;
private static Pane cellsPane;
private static HBox buttonsPane;
private static Pane statsPane;
private static Pane setupsPane;
Label cellsCountLabel;
static int gameWidth= 700;
static int gameHeight=600;
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Hello World");
gamePane = new BorderPane();
buttonsPane = new HBox(5);
statsPane = new Pane();
cellsPane = makeGrid(20);
setupsPane = new Pane();
cellsPane.setStyle("-fx-background-color: #ffffff; -fx-border-color: black");
buttonsPane.setStyle("-fx-background-color: #f2f2f2; -fx-border-color: black");
statsPane.setStyle("-fx-background-color: #f2f2f2; -fx-border-color: black");
setupsPane.setStyle("-fx-background-color: #f2f2f2; -fx-border-color: black");
cellsPane.setMaxWidth(400);
statsPane.setMaxWidth((gameWidth-400)/2);
setupsPane.setMaxWidth((gameWidth-400)/2);
createCellButton = new Button();
deleteCellsButton = new Button();
createCellButton.setText("Create a cell");
deleteCellsButton.setText("Delete cells");
...
buttonsPane.setSpacing(10);
buttonsPane.setPadding(new Insets(10, 10, 10, 10));
buttonsPane.getChildren().add(createCellButton);
buttonsPane.getChildren().add(deleteCellsButton);
gamePane.setTop(buttonsPane);
gamePane.setCenter(cellsPane);
...
cellsCountLabel = new Label("Cells Count: " + (cellId + 1));
statsPane.getChildren().add(cellsCountLabel);
gamePane.setLeft(statsPane);
setupsPane.getChildren().add(new Label("Setups Panel"));
gamePane.setRight(setupsPane);
gamePane.setMargin(statsPane, new Insets(0, 0, 0, 0));
gamePane.setMargin(cellsPane, new Insets(0,0,0,0));
gamePane.setMargin(setupsPane, new Insets(0,0,0,0));
Scene scene = new Scene(gamePane, gameWidth, gameHeight);
primaryStage.setScene(scene);
...
primaryStage.show();
}
...
}
However, there exists space between left, center and right Panes. The space gets smaller as the content in those Panes get larger, however, when it gets too large it messes with the center Pane.
Each Pane has borders to see its limits more clearly. I want the Panes to take up exactly 150, 400 and 150 space, without empty spaces inbetween and also without invading center's Pane border.
I tried using gamePane.setMargin(..., new Insets(0, 0, 0, 0)); but it didn't seem to work.

JavaFX ScrollPane setVvalue() not working properly

I am testing the JavaFX ScrollPane class and realized that it is not working as I expect, I don't know why. I have the following code:
public class Client3 extends Application {
int indexMsg = 0;
Button send;
GridPane root;
ScrollPane msgPane;
GridPane msgPaneContent;
FlowPane writePane;
TextField writeMsg;
Scene scene;
#Override
public void start(Stage primaryStage) {
root = new GridPane();
root.setAlignment(Pos.CENTER);
root.setVgap(10);
root.setPadding(new Insets(10, 10, 10, 10));
msgPane = new ScrollPane();
msgPane.setPrefSize(280, 280);
msgPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
msgPaneContent = new GridPane();
msgPaneContent.setPrefWidth(270);
msgPaneContent.setVgap(10);
writePane = new FlowPane(10, 10);
writePane.setAlignment(Pos.CENTER);
writePane.setPrefWidth(280);
writeMsg = new TextField();
writeMsg.setPrefWidth(150);
writeMsg.setPromptText("Write your message");
writePane.getChildren().add(writeMsg);
GridPane.setConstraints(msgPane, 0, 0);
GridPane.setConstraints(writePane, 0, 1);
msgPane.setContent(msgPaneContent);
root.getChildren().addAll(msgPane, writePane);
writeMsg.setOnAction((ev) -> {
if (!writeMsg.getText().isEmpty()) {
TextArea msg = new TextArea(writeMsg.getText());
msg.setMaxWidth(135);
msg.setPrefRowCount(msg.getLength() / 21 + 1);
msg.setWrapText(true);
GridPane.setConstraints(msg, 0, indexMsg);
indexMsg++;
writeMsg.deleteText(0, writeMsg.getText().length());
msgPaneContent.getChildren().add(msg);
msgPane.setVvalue(1.0);
}
});
scene = new Scene(root, 300, 300);
primaryStage.setTitle("Chat App");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Basically, I have a GridPane as the root with a ScrollPane and a GridPane as its children. The ScrollPane has a children GridPane. There is a TextField with an EventHandler which generates a TextArea inside the GridPane (the ScrollPane's children). Each TextArea object is created in the vertical direction, downwards. I want to set the scrollbar always at its maximum value (setVvalue(1.0)) each time a new TextArea is added. The thing is that it doesn't seem to work as it should because the vertical value is never set to the maximum after handling the event, but it seems to be set to the maximum value that it had before handling it (the bottom of the previous TextArea added).
Any solution for this? Thanks in advance.

JavaFX Node partial border

Having a Node for example VBox I am trying to add a border and there are 2 ways I can think of - using css or using new Border () etc..
How can I remove part of the border ? i.e remove the bottom part of the border
You can specify different styles for the borders on different sides
Using Border
#Override
public void start(Stage primaryStage) {
Region root = new Region();
root.setBorder(new Border(new BorderStroke(Color.RED, Color.RED, Color.RED, Color.RED,
BorderStrokeStyle.SOLID, BorderStrokeStyle.SOLID, BorderStrokeStyle.NONE, BorderStrokeStyle.SOLID,
CornerRadii.EMPTY, new BorderWidths(5), Insets.EMPTY)));
Scene scene = new Scene(root, 300, 300);
primaryStage.setScene(scene);
primaryStage.show();
}
Using inline css
root.setStyle("-fx-border-style: solid solid none solid; -fx-border-width: 5; -fx-border-color: red;");
Using a css stylesheet
.root { /* modify the selector according to your needs */
-fx-border-style: solid solid none solid;
-fx-border-width: 5;
-fx-border-color: red;
}
none doesnt work on javafx 13. I tried changing it to hidden and it works.
.root { /* modify the selector according to your needs */
-fx-border-style: solid solid hidden solid;
-fx-border-width: 5;
-fx-border-color: red;
}
Setting border-width to 0 worked (JavaFX 17): Example:
#header
{
-fx-border-width: 0 0 2px 0;
-fx-border-color: black;
-fx-border-style: solid;
}
Here you can get border only at bottom - order: Top, right, bottom, left.

JavaFX Change textcolor of disabled textfield in CSS

I have a texfield in a stage:
#FXML
private TextField tfAdUsername;
tfAdUsername.setPromptText(userName);
tfAdUsername.setDisable(true);
The textcolor is lightgray, i want to change it to black:
.text-field:readonly {
-fx-background-color: #E0E0E2;
-fx-border-color: #94BBDA;
-fx-text-fill: #000000;
}
.text-field:disabled {
-fx-background-color: #E0E0E2;
-fx-border-color: #94BBDA;
-fx-text-fill: #000000;
}
This doesn't change the textcolor. What is the correct CSS Property to use?
The reason why color turns grey on disabling is because of the opacity change. Just try adding the following css to your textfield.
-fx-opacity: 1.0;
Working example (using setStyle() )
public class KeyStroke extends Application {
#Override
public void start(Stage primaryStage) {
Pane root = new Pane();
TextField textField = new TextField("Itachi");
textField.setDisable(true);
textField.setStyle("-fx-opacity: 1.0;");
root.getChildren().add(textField);
Scene scene = new Scene(root, 200, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
For changing the opacity use:
.text-input:disabled {
-fx-opacity: 1.0;
}
in the css file.
To change the textcolor used (after i read the question properly i know this wasn't asked.)
From the modena.css stylesheet:
.root {
/* Used for the inside of text boxes, password boxes, lists, trees, and
* tables. See also -fx-text-inner-color, which should be used as the
* -fx-text-fill value for text painted on top of backgrounds colored
* with -fx-control-inner-background.
*/
-fx-control-inner-background: derive(-fx-base,80%);
/* Version of -fx-control-inner-background for alternative rows */
-fx-control-inner-background-alt: derive(-fx-control-inner-background,-2%);
/* One of these colors will be chosen based upon a ladder calculation
* that uses the brightness of a background color. Instead of using these
* colors directly as -fx-text-fill values, the sections in this file should
* use a derived color to match the background in use. See also:
*
* -fx-text-base-color for text on top of -fx-base, -fx-color, and -fx-body-color
* -fx-text-background-color for text on top of -fx-background
* -fx-text-inner-color for text on top of -fx-control-inner-color
* -fx-selection-bar-text for text on top of -fx-selection-bar
*/
}
.root {
-fx-text-inner-color: #FF01F3;
}
For example:
#Override
public void start(Stage primaryStage) {
VBox root = new VBox();
root.setAlignment(Pos.CENTER);
Button btn = new Button();
TextField text= new TextField();
btn.setText("Press me!");
btn.setOnAction((ActionEvent event) -> {
text.setText("Goodbye World");
});
root.getChildren().addAll(btn, text);
Scene scene = new Scene(root, 300, 250);
scene.getStylesheets().addAll(getClass().getResource("css.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
And the color of the text in the textfield will be pink?purple?
This is by the way not a means to change the color for each textfield individually, if you change this for the root then all the textfields will use this color instead of the usual black. I don't know how to change the color for one textfield to blue and for another one in the same application to red.

Categories