JavaFX- Button Height Doesn't Get Smaller? - java

.button
{
-fx-background-image: url("mapDefault.png");
-fx-pref-width: 10px;
-fx-pref-height: 10px;
}
My button looks like this, and theoretically, it should fit the image (10x10) exactly. However
It returns that the button is about 30px in height, despite that I changed it. The button is declared like
Button beachButton = new Button();
and does not have any text values in it.
Any possible causes to this increase/min-cap in height?

Any changes in text need to be taken into account, especially custom font. To accommodate for any picture sizes smaller than the font of the item, set the font size equal to 0px as well.

The offset of the icon(or text) from the buttons border may be the problem. A Button with text in default font type & size cannot get smaller in height than 25px (using setPrefHeight() or setStyle(...)). To make the height the same as other controls the padding could be reduced as in the following example:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class SmallerButtons extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
Label label1= new Label ("Label1");
Label label2= new Label ("Label2");
CheckBox checkBox1= new CheckBox ("CheckBox1");
CheckBox checkBox2= new CheckBox ("CheckBox2");
Button button1= new Button ("Button1");
Button button2= new Button ("Button2");
Button button3= new Button ("Button3");
button3.setOnAction(a-> System.out.println(label1.getHeight() + " / " +checkBox1.getHeight() + " / " +button1.getHeight()));
label1.setStyle("-fx-font-size:10");
button1.setPadding(new Insets(0,3,0,3)); // <----- this one works. Standard value is 5.0.
button1.setMaxWidth(200);
button2.setPrefHeight(17); // no effect
button3.setStyle("-fx-pref-height:35px"); // or also setPrefHeight(35)
VBox vb1=new VBox(label1,label2);
VBox vb2=new VBox(checkBox1,checkBox2);
VBox vb3=new VBox(button1, button2, button3);
vb3.setMaxWidth(80);
vb1.setSpacing(1);
vb2.setSpacing(1);
vb3.setSpacing(1);
HBox hb = new HBox(vb1,vb2,vb3);
hb.setSpacing(15);
Scene scene = new Scene(hb);
stage.setOnCloseRequest((e)->System.exit(0));
stage.setScene(scene);
stage.show();
}
}
One catch: With one buttons padding adjusted all other buttons need the padding explicitly set, too.

Related

How to modify JavaX Button (if decorated with CSS) so it gets darker when needed?

Modify the color of a JavaFX button so it gets darker when I pass the mouse, but when it is decorated with CSS:
When you modify the color of a Button in JavaFX, you lose the effect that makes it darker when the mouse passes. So seeing that we get 2 functions to do some action when the mouse gets over the button setOnMouseEntered/setOnMouseExited I can just get the color and use the function .darker()like this:
private Color last_color = null;
public void change_color(Button button) {
Color color;
if (last_color == null) {
color = (Color)button.getBackground().getFills().get(0).getFill();
last_color = color;
color = color.darker();
} else {
color = last_color;
last_color = null;
}
button.setBackground(new Background(new BackgroundFill(color, CornerRadii.EMPTY, Insets.EMPTY)));
}
This actually works, except when the color of the buttons is in CSS, looks like the CSS style is beyong the background color set with Java code.
The buttons are probably going to be styled with CSS, and I would like to have a generic function to work in any Button. Is possible to get the color from CSS and modify it?
If not, how could I make the Button darker when the mouse is over it?
To change the color of the button on hover, you can add the corresponding css in the CSS file.
.myButton:hover{
-fx-color: derive(-fx-base, -45%);
}
But if you say, you need to do some calculations, or other logic to determine the color, then you see the below approach.The below approach is to give you some idea only. THIS IS NOT A SOLUTION. I always recommend to get things done using CSS.
The key point to note is : the background of a button is made up of many fills. You need to know which fill you need to modify.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.List;
public class ButtonBackgroundDemo extends Application {
public static void main(String[] args) {
launch(args);
}
BackgroundFill lastFill;
#Override
public void start(Stage primaryStage) {
Button button = new Button("Api Test");
button.addEventFilter(MouseEvent.MOUSE_ENTERED, e->changeColor(button, e));
button.addEventFilter(MouseEvent.MOUSE_EXITED, e->changeColor(button, e));
Button button2 = new Button("Css Test");
button2.getStyleClass().add("myButton");
VBox root = new VBox(button,button2);
root.setAlignment(Pos.CENTER);
root.setSpacing(20);
Scene scene = new Scene(root, 200, 200);
scene.getStylesheets().add(getClass().getResource("button.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* I really think this is an overkill. Instead, use CSS implementation
*/
private void changeColor(Button button, MouseEvent e) {
int fillSize = button.getBackground().getFills().size();
List<BackgroundFill> fills = new ArrayList<>();
button.getBackground().getFills().forEach(fills::add);
if(e.getEventType() == MouseEvent.MOUSE_ENTERED){
lastFill = fills.get(fillSize-1);
fills.remove(lastFill);
// Use your logic to darken the lastFill.getFill(). This may be Linear-Gradient as well. so get the correct color from gradient as well.
BackgroundFill fill = new BackgroundFill(Color.DARKGREY, lastFill.getRadii(),lastFill.getInsets());
fills.add(fill);
}else {
fills.remove(fillSize-1);
fills.add(lastFill);
}
button.setBackground(new Background(fills,null));
}
}
And in the button.css:
.myButton:hover{
-fx-color: derive(-fx-base, -45%);
}

Divide stage into 2 gridpanes JavaFX

So Im trying to have text on the left and buttons on the right, text should have constant size and buttons should resize to fill the rest of the window.
Here is my result so far:
I dont want my text over buttons, I want them to share the whole window.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
GridPane buttons = new GridPane();
GridPane textGrid = new GridPane();
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
Button button1 = new Button();
Button button2 = new Button();
Button button3 = new Button();
Button button4 = new Button();
Button button5 = new Button();
button1.setText("Button1");
button2.setText("Button4");
button3.setText("Button3");
button4.setText("Button4");
button5.setText("Button5");
TextArea text1 = new TextArea();
text1.setText("Test");
text1.setPrefSize(100, 100);
button1.prefWidthProperty().bind(buttons.widthProperty());
button2.prefWidthProperty().bind(buttons.widthProperty());
button3.prefWidthProperty().bind(buttons.widthProperty());
button4.prefWidthProperty().bind(buttons.widthProperty());
button5.prefWidthProperty().bind(buttons.widthProperty());
button1.prefHeightProperty().bind(buttons.heightProperty());
button2.prefHeightProperty().bind(buttons.heightProperty());
button3.prefHeightProperty().bind(buttons.heightProperty());
button4.prefHeightProperty().bind(buttons.heightProperty());
button5.prefHeightProperty().bind(buttons.heightProperty());
buttons.addColumn(0, button1, button2, button3, button4, button5);
textGrid.addColumn(0, text1);
Scene scene = new Scene(root, 280, 180);
root.getChildren().addAll(buttons, textGrid);
buttons.setAlignment(Pos.TOP_RIGHT);
textGrid.setAlignment(Pos.TOP_LEFT);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
It is usually better to let the layout panes handle the layout management rather than trying to manage the layout through bindings.
Here is a sample:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.util.stream.IntStream;
public class Main extends Application {
private static final int N_BUTTONS = 5;
#Override
public void start(Stage stage) {
VBox buttonLayout = new VBox(
10,
IntStream.range(0, N_BUTTONS)
.mapToObj(this::createButton)
.toArray(Button[]::new)
);
HBox.setHgrow(buttonLayout, Priority.ALWAYS);
TextArea textArea = new TextArea("Test");
textArea.setPrefWidth(100);
textArea.setMaxWidth(TextArea.USE_PREF_SIZE);
textArea.setMinWidth(TextArea.USE_PREF_SIZE);
HBox layout = new HBox(10, textArea, buttonLayout);
layout.setPadding(new Insets(10));
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
private Button createButton(int i) {
Button button = new Button("Button " + i);
// button.setMaxWidth(Double.MAX_VALUE);
button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
VBox.setVgrow(button, Priority.ALWAYS);
return button;
}
public static void main(String[] args) {
launch(args);
}
}
Here are a couple of things I would point out based upon the sample:
As the buttons are so similar, create the buttons in a loop rather than individually in code. I use an IntStream range with a map and a toArray, but you could do the same thing with a standard for loop (which may be easier to understand).
Use combinations of standard layout panes to achieve your layout. For example the buttons are vertically spaced, so put them in a VBox, the text and the buttons are horizontal to each other, so use a HBox.
Use constraints on the layouts to massage them into performing the layout you like, for example, HBox.setHgrow(buttonLayout, Priority.ALWAYS); tells the Box to always assign any extra additional space in the Box to the buttonLayout so that the buttons will fill any remaining area.
Set constraints on the individual nodes to size them how you wish, for example the following code establishes a fixed width for the textArea, which will not vary (you could similar code to establish a fixed height if you wished):
textArea.setPrefWidth(100);
textArea.setMaxWidth(TextArea.USE_PREF_SIZE);
textArea.setMinWidth(TextArea.USE_PREF_SIZE);
Some controls will automatically expand themselves beyond their max size, buttons do not by default, to enable this behavior use the following code (if you only wanted the width to expand and not the height then you would only set the maxWidth rather than the maxSize):
button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
Rather than defining layouts in code as in this example, instead use a tool such as SceneBuilder to create the scene visually and save the layout as an FXML file, so that the layout is separated from your code (similarly place any styling in an external CSS file).

JavaFX How to Vertically Align CheckBox in an HBox in a GridPane

First, code that generates a UI that illustrates the problem:
package test;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(final String[] args) throws Exception {
launch(args);
}
#Override
public void start(final Stage window) throws Exception {
// Create a VBox to hold the table and button
final GridPane root = new GridPane();
root.setHgap(5);
root.setVgap(5);
// Add a combo-box to the first row
final ComboBox<String> dropdown1 = new ComboBox<>();
dropdown1.getItems().add("Option 1");
dropdown1.getSelectionModel().selectFirst();
root.add(dropdown1, 0, 0);
// Add a checkbox to the first row
final CheckBox checkbox1 = new CheckBox("CB Text 1");
root.add(checkbox1, 1, 0);
// Add a combo-box to the second row
final ComboBox<String> dropdown2 = new ComboBox<>();
dropdown2.getItems().add("Option 2");
dropdown2.getSelectionModel().selectFirst();
root.add(dropdown2, 0, 1);
// Add a checkbox, wrapped in an HBox, to the second row
final CheckBox checkbox2 = new CheckBox("CB Text 2");
final HBox hbox = new HBox(checkbox2);
hbox.setAlignment(Pos.BASELINE_LEFT);
root.add(hbox, 1, 1);
GridPane.setValignment(hbox, VPos.BASELINE);
// Show the JavaFX window
final Scene scene = new Scene(root);
window.setScene(scene);
window.show();
}
}
The above code generates the following UI (Java 8u102 Windows x64):
As shown in the image, the vertical alignment of the CheckBox in the second row is misaligned with the ComboBox. I expect everything to be aligned on the text baseline. How can I get the second row in the GridPane to match the alignment of the first row, without removing the HBox?
Modify the code that populates the offending cell to be the following:
// Add a checkbox, wrapped in an HBox, to the second row
final CheckBox checkbox2 = new CheckBox("CB Text 2");
final HBox hbox = new HBox(checkbox2);
hbox.setFillHeight(true); // Added this
hbox.setAlignment(Pos.CENTER_LEFT);// Changed the alignment to center-left
root.add(hbox, 1, 1);
//GridPane.setValignment(hbox, VPos.BASELINE); This is unnecessary
This code will force the HBox to be the same height as the row, then vertically center the CheckBox within it.

FX - Suggestions for aligning CheckBox in Grid

Converting from Swing to FX here. We have some CheckBoxes where the Label appears on the left side of the CheckBox. We are accomplishing this by calling
setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT);
The problem is that in some GridPanes, we'll have a Label in column 0 with a Node in column 1 on row 1, and then one of the CheckBoxes in the second row. Ideally the CheckBox and Node are aligned with each other. I am currently accomplishing this by setting the CheckBox's columnSpan to 2, and adding some right padding to align the fields. Is there an easier way to be doing this?
Our previous solution was to just separate the Label from the CheckBox, however this caused us to lose the functionality of selecting/deselecting the CheckBox upon clicking the label.
EDIT:
I'm trying to figure out the best way to align the CheckBox with the Field.
I can't see a "nice" way to do what you want: the best I can come up with is to separate the label from the check box, and register a mouse listener with the label to toggle the state of the check box. Maybe someone else can see a more elegant way to do this.
SSCCE:
import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;
public class AlignedCheckBox extends Application {
#Override
public void start(Stage primaryStage) {
GridPane grid = new GridPane();
Label checkboxLabel = new Label("Selected:");
CheckBox checkBox = new CheckBox();
checkboxLabel.setLabelFor(checkBox);
checkboxLabel.setOnMouseClicked(e -> checkBox.setSelected(! checkBox.isSelected()));
Label textFieldLabel = new Label("Enter text:");
TextField textField = new TextField();
grid.addRow(0, checkboxLabel, checkBox);
grid.addRow(1, textFieldLabel, textField);
ColumnConstraints leftCol = new ColumnConstraints();
leftCol.setHalignment(HPos.RIGHT);
leftCol.setHgrow(Priority.SOMETIMES);
ColumnConstraints rightCol = new ColumnConstraints();
rightCol.setHalignment(HPos.LEFT);
rightCol.setHgrow(Priority.ALWAYS);
grid.getColumnConstraints().addAll(leftCol, rightCol);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(20));
Scene scene = new Scene(grid);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFx 8 - A class that has a border than you're able to color and able to have text in the middle of it

Does anyone know of a class that i can use that is essential a rectangle, BUT it has text in the middle of the rectangle and the rectangle has a fill color along with a border color(the border can be changed to red or something along those lines)
Essentially right now i have a pane, and i want to make a 2D grid(10x10), where each individual object in the grid is a rectangle-typed object that has a number text center justified, a fill color, and a border color.
Note: I've tried to use gridpane, but the lack of documentation that i've found has led me to believe i can only set the fill color, and each cell in the grid pane does NOT look like a separate object like i want it to. I've also tried to implement rectangle but the rectangle doesn't have text or a border that i can manipulate.
Thank you for your help.
Just use a Label. Here's a proof of concept:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class CustomLabelDemo extends Application {
#Override
public void start(Stage primaryStage) {
Label label = new Label("Hello World");
label.setStyle(
"-fx-alignment: center;"
+"-fx-padding: 6px;"
+"-fx-background-color: red, -fx-background;"
+"-fx-background-insets: 0, 4px;"
);
StackPane root = new StackPane(label);
primaryStage.setScene(new Scene(root, 350, 75));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The way the css is working here is that it defines two backgrounds: the first (and thus the one at the back) is red; the one in front is set to -fx-background, which is the color defined in the default stylesheet for the background of most controls. Corresponding to these are two insets for the two backgrounds: the first set to zero, and the second set to 4 pixels. This means that 4 pixels of the red border will be visible. The padding is just set to make sure the text doesn't overlap the outer background (the border).
In a real application, you should put the style in an external file. You can also define a "looked-up-color" for the border color; this will make it much easier to change the color at runtime:
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.VBox;
import javafx.stage.Stage;
public class CustomLabelDemo extends Application {
#Override
public void start(Stage primaryStage) {
Label label = new Label("Hello World");
label.getStyleClass().add("custom-label");
Button changeColorButton = new Button("Change to green");
changeColorButton.setOnAction(event -> label.setStyle("custom-label-border-color: green;"));
VBox root = new VBox(10, label, changeColorButton);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 350, 75);
scene.getStylesheets().add("custom-label.css");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
with custom-label.css:
.custom-label {
custom-label-border-color: red ;
-fx-alignment: center;
-fx-padding: 6px;
-fx-background-color: custom-label-border-color, -fx-background;
-fx-background-insets: 0, 4px;
}
.button {
-fx-alignment: center ;
}
If you have a fixed set of states that the colors represent, you might want to use a pseudoclass to represent the state:
import javafx.application.Application;
import javafx.css.PseudoClass;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class CustomLabelDemo extends Application {
#Override
public void start(Stage primaryStage) {
Label label = new Label("Hello World");
label.getStyleClass().add("custom-label");
CheckBox errorCheckBox = new CheckBox("Error");
PseudoClass errorState = PseudoClass.getPseudoClass("error");
errorCheckBox.selectedProperty().addListener((obs, wasSelected, isNowSelected) ->
label.pseudoClassStateChanged(errorState, isNowSelected));
VBox root = new VBox(10, label, errorCheckBox);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 350, 75);
scene.getStylesheets().add("custom-label.css");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
custom-label.css:
.custom-label {
custom-label-border-color: green ;
-fx-alignment: center;
-fx-padding: 6px;
-fx-background-color: custom-label-border-color, -fx-background;
-fx-background-insets: 0, 4px;
}
.custom-label:error {
custom-label-border-color: red ;
}
.check-box {
-fx-alignment: center ;
}
I think your best bet is to make a custom control, exposing the properties you need.
See http://docs.oracle.com/javafx/2/fxml_get_started/custom_control.htm for info and examples.

Categories