How to hide the down arrow button on a combobox in javafx? - java

I have an editable ComboBox and I wish to "hide" the down arrow button so that it looks like a normal textbox.

Use css as,
.combo-box .arrow, .combo-box .arrow-button{
-fx-background-color: transparent;
}
Sample code::
public class ComboboxSample extends Application {
public static void main(String[] args) {
launch(args);
}
final Button button = new Button("Send");
final Label notification = new Label();
final TextField subject = new TextField("");
final TextArea text = new TextArea("");
String address = " ";
#Override
public void start(Stage stage) {
stage.setTitle("ComboBoxSample");
Scene scene = new Scene(new Group(), 450, 250);
// Load the css as below to the scene
scene.getStylesheets()
.add(ComboboxSample.class.getResource("styles.css").toExternalForm());
final ComboBox emailComboBox = new ComboBox();
emailComboBox.setEditable(true);
emailComboBox.getItems().addAll("jacob.smith#example.com",
"isabella.johnson#example.com", "ethan.williams#example.com",
"emma.jones#example.com", "michael.brown#example.com");
final ComboBox priorityComboBox = new ComboBox();
priorityComboBox.getItems().addAll("Highest", "High", "Normal", "Low",
"Lowest");
priorityComboBox.setValue("Normal");
GridPane grid = new GridPane();
grid.setVgap(4);
grid.setHgap(10);
grid.setPadding(new Insets(5, 5, 5, 5));
grid.add(new Label("To: "), 0, 0);
grid.add(emailComboBox, 1, 0);
grid.add(new Label("Priority: "), 2, 0);
grid.add(priorityComboBox, 3, 0);
grid.add(new Label("Subject: "), 0, 1);
grid.add(subject, 1, 1, 3, 1);
grid.add(text, 0, 2, 4, 1);
grid.add(button, 0, 3);
grid.add(notification, 1, 3, 3, 1);
Group root = (Group) scene.getRoot();
root.getChildren().add(grid);
stage.setScene(scene);
stage.show();
}
}
Output:

.combo-box1 .arrow-button
{
-fx-background-color: null;
-fx-background-insets: 0;
-fx-background-radius: 0;
-fx-padding: 0.0em 0em 0.0em 0.0em; /* 0 3 0 0 */
}
.combo-box1 .arrow-button .arrow
{
-fx-background-color: -fx-mark-highlight-color, -fx-mark-color;
-fx-background-insets: 0 0 0 0, 0;
-fx-padding: 0em 0em 0em 0em; /* 3 3.75 3 3.75 */
/*
-fx-shape: "M 0 0 h 7 l -3.5 4 z";
*/
-fx-shape: null;
}

I used the following ccs code:
.combo-box1 .arrow-button {
-fx-opacity: 0.0;
-fx-cursor: text;
}
and this in java:
combobox.getStyleClass().add("combo-box1");

There are several ways to do this, here are four. The code is Jython with JavaFX.
First, the enum, for context.
public enum URLBarArrowConstants {
//URLBarArrow Constants
BYCSS_AND_SHAPE,
BYCSS_AND_NO_SHAPE,
NOCSS_AND_SHAPE,
NOCSS_AND_NO_SHAPE;
}
Second, the css files, for context.
EG #1
/*ComboBox's Arrow is a Region.*/
.combo-box .arrow-button .arrow {
-fx-shape: "...";
-fx-scale-shape: true;
-fx-position-shape: true;
}
EG#2
/*ComboBox's Arrow is a Region.*/
.combo-box .arrow-button .arrow {
/*Setting either of these two will do.*/
-fx-background-color: transparent;
-fx-opacity: 0.0;
}
/*ComboBox's Arrow Button is a Stack Pane.*/
.combo-box .arrow-button{
-fx-background-position: center;
-fx-background-repeat: no-repeat;
-fx-background-image: url("..<file>.png");
}
The method, in my main file.
def setCustomURLBarArrow(self, url_bar, scene, URLBarArrowConstant):
from javafx.scene.paint import Paint
from javafx.scene.shape import Shape, SVGPath, FillRule
Don't configure the ComboBox Arrow by CSS, instead, do it programmatically and change the Regions SVG Shape
if URLBarArrowConstant == URLBarArrowConstants.NOCSS_AND_SHAPE:
#SVG Object
previous_url_bar = SVGPath()
#SVG Path
previous_url_bar.setContent("...") # edit this
#SVG Fill Rule
previous_url_bar.setFillRule(FillRule.NON_ZERO)
#Set Fill --
previous_url_bar.setFill(Paint.valueOf(Color.web("...").toString())) //edit here
#Apply CSS Sheet
url_bar.applyCss()
#Set Region's Shape
arrow_region = url_bar.lookup(".arrow").setShape(previous_url_bar)
Configure the ComboBox Arrow by CSS and change the Regions SVG Shape
elif URLBarArrowConstant == URLBarArrowConstants.BYCSS_AND_SHAPE:
#Apply Stylesheet for URL Bar
scene.getStylesheets().add(File("..<file>.css").toURI().toString()) //edit here
Configure the ComboBox Arrow by CSS but instead, merely hide the arrow by setting the transparency/opacity values and set a background.
elif URLBarArrowConstant == URLBarArrowConstants.BYCSS_AND_NO_SHAPE:
#Apply Stylesheet for URL Bar
scene.getStylesheets().add(File("..<file>.css").toURI().toString()) //edit here
Don't configure the ComboBox Arrow by CSS, instead, do it programmatically and merely hide the arrow by setting the transparency/opacity values and set a background.
elif URLBarArrowConstant == URLBarArrowConstants.NOCSS_AND_NO_SHAPE:
from javafx.scene.paint import Paint
from javafx.scene.layout import CornerRadii
from javafx.scene.layout import Background, BackgroundSize, BackgroundImage, BackgroundPosition, BackgroundRepeat, BackgroundFill
#Apply CSS Sheet
url_bar.applyCss()
#Grab Arrow(Region), ArrowButton(StackPane) ComboBox properties
arrow_region = url_bar.lookup(".arrow")
arrow_button = url_bar.lookup(".arrow-button")
#Either Set Opacity to 0 or set background color to transparent.
arrow_region.setOpacity(0.0)
arrow_region.setBackground( Background( array(BackgroundFill, [BackgroundFill( Paint.valueOf(Color.TRANSPARENT.toString()), CornerRadii.EMPTY, Insets.EMPTY)]) ) )
#Set a Background Image for the .arrow-button StackPane.
arrow_button.setBackground(Background( array(BackgroundImage, [BackgroundImage( Image( String(File('..<file>.png').toURI().toString()), True) , BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.CENTER, BackgroundSize.DEFAULT)] ) ) ) //if you want, edit this

Related

How to flip axis of javafx slider

I'm creating rulers using javafx by modifying css of javafx slider and I created something like this:
And I was trying to make this:
So I tried to rotate sliders by calling setRotate() method but it becomes like this:
Here is my code for sliders:
Slider hRuler = new Slider(0, 160, 10);
hRuler.showTickMarksProperty().setValue(true);
hRuler.showTickLabelsProperty().setValue(true);
hRuler.setRotate(180);
Slider vRuler = new Slider(0, 100, 10);
vRuler.setOrientation(Orientation.VERTICAL);
vRuler.showTickMarksProperty().setValue(true);
vRuler.showTickLabelsProperty().setValue(true);
vRuler.setRotate(180);
And here is my css code for sliders:
.slider > .thumb,
.slider > .thumb:hover,
.slider:focused > .thumb{
-fx-background-color: #ff6a6a;
-fx-background-insets: 2 0 -23 0;
-fx-padding: 1 1 0 1;
-fx-background-radius: 0;
}
.slider:vertical > .thumb,
.slider:vertical > .thumb:hover,
.slider:vertical:focused > .thumb{
-fx-background-color: #ff6a6a;
-fx-background-insets: 0 -23 0 2;
-fx-padding: 1 0 1 1;
-fx-background-radius: 0;
}
.slider > .track,
.slider:vertical > .track {
-fx-background-color: transparent;
-fx-background-insets: 0;
-fx-background-radius: 0;
-fx-padding: 0;
}
.slider > .axis {
-fx-tick-mark-stroke: transparent;
-fx-tick-label-font-size: 0.833333em;
-fx-tick-label-fill: #9a9a9a;
-fx-background-color: #333;
}
Please suggest me how can I flip axis or rotate labels of these slider so that I can achieve expected results.
Basically, you have to set the side property of the axis (for left and top). The steps that are involved:
let the slider have a custom style, f.i. axis-top/axis-left
in css, define a rule for the axis contained in such a slider to set its side to top/left
The code:
Slider hRuler = new Slider(0, 160, 10);
hRuler.showTickMarksProperty().setValue(true);
hRuler.showTickLabelsProperty().setValue(true);
hRuler.getStyleClass().add("axis-top");
Slider vRuler = new Slider(0, 100, 10);
vRuler.setOrientation(Orientation.VERTICAL);
vRuler.showTickMarksProperty().setValue(true);
vRuler.showTickLabelsProperty().setValue(true);
vRuler.getStyleClass().add("axis-left");
In css:
.slider.axis-top > .axis {
-fx-side: TOP;
}
.slider.axis-left > .axis {
-fx-side: LEFT;
}
This can certainly be optimized, but should get you started.

How to add text to each face of a box [JavaFX]

I create a box which I can rotate and what will do ~some action~ when clicked. The problem I'm having is display text on all the faces of this box, for example;
1 on the front, 2 on the top, 3 on the back, 4 on the bottom, 5 on the left and 6 on the right.
I understand that StackPane can be used to overlay a text box on-top of the cube but I don't think that'd really help in this scenario.
Since box is essentially a pre-constructed TriangleMesh, is this possible to do?
As far as seen, box doesn't have any in-built functionality to do this.
static double mousePosX;
static double mousePosY;
static double mouseOldX;
static double mouseOldY;
public static Scene testScene(Stage stage) {
Group root = new Group();
Scene scene = new Scene(root, stage.getWidth(), stage.getHeight(), true, SceneAntialiasing.BALANCED);
scene.setFill(Paint.valueOf("Blue"));
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setNearClip(0.1);
camera.setFarClip(10000.0);
camera.setTranslateZ(-10);
scene.setCamera(camera);
Box box = new Box(1,1,1);
box.setOnMouseClicked(e -> {
System.out.println("Test");
});
Rotate rotateX = new Rotate(10, 0, 0, 0, Rotate.X_AXIS);
Rotate rotateY = new Rotate(5, 0, 0, 0, Rotate.Y_AXIS);
box.getTransforms().addAll(rotateX, rotateY);
scene.setOnMousePressed(me -> {
mouseOldX = me.getSceneX();
mouseOldY = me.getSceneY();
});
scene.setOnMouseDragged(me -> {
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
rotateX.setAngle(rotateX.getAngle() - (mousePosY - mouseOldY));
rotateY.setAngle(rotateY.getAngle() + (mousePosX - mouseOldX));
mouseOldX = mousePosX;
mouseOldY = mousePosY;
});
root.getChildren().add(box);
return scene;
}
This is the code I've got so far and any assistance would be greatly appreciated.
This solution is based in the answer to this question, where the CuboidMesh from the FXyz library is used.
The main idea is to use an image as texture for the cube. The built-in JavaFX Box will apply this image to each of the 6 faces, so if we want to have different text in each face, we have to use the CuboidMesh, that makes use of the net image:
The cube can be generated as:
CuboidMesh cuboid = new CuboidMesh(100f, 100f, 100f);
cuboid.setTextureModeImage(getClass().getResource("net.png").toExternalForm());
The idea now is to write the text in each of the 6 faces and save the texture image that will be used later on.
This method will generate this net image:
private Image generateNet(String face1, String face2, String face3, String face4, String face5, String face6) {
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
Label label1 = new Label(face1);
label1.setRotate(90);
GridPane.setHalignment(label1, HPos.CENTER);
Label label2 = new Label(face2);
GridPane.setHalignment(label2, HPos.CENTER);
Label label3 = new Label(face3);
GridPane.setHalignment(label3, HPos.CENTER);
Label label4 = new Label(face4);
GridPane.setHalignment(label4, HPos.CENTER);
Label label5 = new Label(face5);
GridPane.setHalignment(label5, HPos.CENTER);
Label label6 = new Label(face6);
label6.setRotate(90);
GridPane.setHalignment(label6, HPos.CENTER);
grid.add(label1, 1, 0);
grid.add(label2, 0, 1);
grid.add(label3, 1, 1);
grid.add(label4, 2, 1);
grid.add(label5, 3, 1);
grid.add(label6, 1, 2);
grid.setGridLinesVisible(true);
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth(25);
ColumnConstraints col2 = new ColumnConstraints();
col2.setPercentWidth(25);
ColumnConstraints col3 = new ColumnConstraints();
col3.setPercentWidth(25);
ColumnConstraints col4 = new ColumnConstraints();
col4.setPercentWidth(25);
grid.getColumnConstraints().addAll(col1, col2, col3, col4);
RowConstraints row1 = new RowConstraints();
row1.setPercentHeight(33.33);
RowConstraints row2 = new RowConstraints();
row2.setPercentHeight(33.33);
RowConstraints row3 = new RowConstraints();
row3.setPercentHeight(33.33);
grid.getRowConstraints().addAll(row1, row2, row3);
grid.setPrefSize(600, 450);
Scene tmpScene = new Scene(grid);
tmpScene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
return grid.snapshot(null, null);
}
where style.css contains:
.root {
-fx-background-color: white;
}
.label {
-fx-font-size: 6em;
}
With it, the labels size and font can be adjusted properly.
Now you can generate a net image for any text:
Image net = generateNet("1", "2", "3", "4", "5", "6");
Finally, you can apply this texture to the cuboid:
PhongMaterial mat = new PhongMaterial();
mat.setDiffuseMap(net);
cuboid.setMaterial(mat);
And you will have your text applied:

JavaFX autoresize & auto-position

I have a VBox which contains Panes. Over the VBox, I want to show another element (currently I use a Pane). This element has to overlay multiple Panes in the VBox, so I put this element together with the VBox inside an AnchorPane. The complete structure looks like this:
HBox
VBox
Label <--- "Friday"
AnchorPane
VBox <--- the VBox with Panes
Pane
...
Pane <--- the Pane over the VBox (red in images)
The problem is that when I resize the window, the red Pane does not resize nor change its position.
Normal size:
Small size:
I want the red Pane to start at the same line (3rd) and be of the same relative size.
I tried binding the Pane's prefWidthProperty and prefHeightProperty to its parent (AnchorPane). This works for auto-resizing.
For auto-positioning, I have tried to bind the layoutX|Y properties, which didn't work, because these are set by the system and I get exception "Bound value cannot be set". So I tried to make the Pane unmanaged, which in turn broke the auto-resize, since in unmanaged nodes, changes in preferred properties have no effect. I thought of binding widthProperty and heightProperty to the parent's properties, but these are read-only.
Consider using a GridPane for functionality like this. You can add multiple nodes to the same cell(s) in the grid (the ones added last will appear on top in z-order). A GridPane allows maximum flexibility for layout. Here's an example: note that there's a lot of styling here that I just hard-coded for brevity, but in a real app you should move this to an external stylesheet (you can do the same with the max sizes, etc):
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Region;
import javafx.scene.layout.RowConstraints;
import javafx.stage.Stage;
public class CalendarExample extends Application {
#Override
public void start(Stage primaryStage) {
GridPane calendar = new GridPane();
// headers:
for (DayOfWeek dayOfWeek = DayOfWeek.MONDAY ; dayOfWeek.getValue() <= DayOfWeek.FRIDAY.getValue();
dayOfWeek = DayOfWeek.of(dayOfWeek.getValue() + 1) ) {
Label label = new Label(dayOfWeek.toString());
label.setAlignment(Pos.CENTER);
label.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
label.setStyle("-fx-background-color: black, darkgray; -fx-background-insets: 0, 0 0 1 1 ;");
calendar.add(label, dayOfWeek.getValue(), 0);
}
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm");
int rowCount = 0 ;
for (LocalTime time = LocalTime.of(8, 0); time.isBefore(LocalTime.of(17, 0)); time=time.plusMinutes(30)) {
rowCount++ ;
Label label = new Label(timeFormatter.format(time));
label.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
label.setAlignment(Pos.CENTER);
String color = rowCount % 2 == 0 ? "darkgray" : "lightgray" ;
label.setStyle("-fx-background-color: black, "+color+"; -fx-background-insets: 0, 0 0 1 1;");
calendar.add(label, 0, rowCount);
}
// cells:
for (int x = 1 ; x <= 5; x++) {
for (int y = 1 ; y <= rowCount; y++) {
Region region = new Region();
String color = y % 2 == 0 ? "darkgray" : "lightgray" ;
region.setStyle("-fx-background-color: black, "+color+"; -fx-background-insets: 0, 0 0 1 1 ;");
calendar.add(region, x, y);
}
}
// Column constraints:
for (int x = 0 ; x <= 5 ; x++) {
ColumnConstraints cc = new ColumnConstraints();
cc.setPercentWidth(100.0 / 6);
cc.setFillWidth(true);
calendar.getColumnConstraints().add(cc);
}
// row constraints:
for (int y = 0 ; y <= rowCount; y++) {
RowConstraints rc = new RowConstraints();
rc.setPercentHeight(100.0 / (rowCount+1));
rc.setFillHeight(true);
calendar.getRowConstraints().add(rc);
}
// Example appointment block:
DayOfWeek appointmentDay = DayOfWeek.FRIDAY ;
LocalTime startTime = LocalTime.of(10, 0);
LocalTime endTime = LocalTime.of(13, 0);
String appointmentText = "Fridays need really long coffee breaks";
Label appointment = new Label(appointmentText);
appointment.setTooltip(new Tooltip(appointmentText));
appointment.setWrapText(true);
appointment.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
appointment.setStyle("-fx-background: red; -fx-background-color: -fx-background; " );
GridPane.setMargin(appointment, new Insets(2, 5, 2, 2));
Scene scene = new Scene(calendar, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
This also behaves as needed when the window is resized:
width and height properties may be readonly, but you can assign the prefered sizes and use autosize.
Example
The following code makes the overlay start at 50% height and have a height of 1/3 of the VBox height:
#Override
public void start(Stage primaryStage) {
Region region1 = new Region();
VBox.setVgrow(region1, Priority.ALWAYS);
region1.setStyle("-fx-background-color: blue;");
Region region2 = new Region();
VBox.setVgrow(region2, Priority.ALWAYS);
region2.setStyle("-fx-background-color: lime;");
Region regionOverlay = new Region();
regionOverlay.setStyle("-fx-background-color: red;");
regionOverlay.setManaged(false);
VBox root = new VBox(region1, region2, regionOverlay);
root.layoutBoundsProperty().addListener((observable, oldValue, newValue) -> {
regionOverlay.setPrefSize(newValue.getWidth() - 20, newValue.getHeight() / 3);
regionOverlay.setLayoutX(10);
regionOverlay.setLayoutY(newValue.getHeight() / 2);
regionOverlay.autosize();
});
root.setPrefSize(400, 400);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
If all you need is a red rectangle, i'd advise you to use the Rectangle shape instead of panes. This allows you to bind its widths and heights explicitly.

JavaFx path transition in gridPane

I can not understand how moveTo method work in javafx. here is my example
I have a GridPane that consist of 4 columns and 1 row. All the columns h has a StackPane and the last Stackpane also has an empty label.
public class PathTransitionTExample extends Application {
StackPane pane;
GridPane grid;
Label label;
Scene scene;
#Override
public void start(Stage primaryStage) {
label = new Label();
label.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
grid = new GridPane();
grid.getStyleClass().add("gridPane");
for (int x = 0; x < 4; x++) {
grid.add(pane = new StackPane(), x, 0);
pane.setPrefSize(50, 50);
pane.getStyleClass().add("stackPane");
}
pane = (StackPane) grid.getChildren().get(3);
pane.getChildren().add(label);
scene = new Scene(grid, 260, 50);
scene.getStylesheets().add(PathTransitionTest.class.getResource("pathCSS.css").toExternalForm());
scene.setOnMouseClicked(me -> pathTransition());
primaryStage.setTitle("Path Transition");
primaryStage.setScene(scene);
primaryStage.show();
}
//pathCSS.css
.gridPane{
-fx-background-color: red;
-fx-hGap: 20;
}
.stackPane{
-fx-background-color: orange;
}
.label {
-fx-background-color: blue;
}
I want to move the label in the grid pane from 0.3 to 0.0 however when I am trying to do that the whole transition slips.
private void pathTransition() {
//the Coordinates of the label
double oldMinX = grid.getChildren().get(3).getLayoutX();
double oldMinY = grid.getChildren().get(3).getLayoutY();
//the coordinates of the stack pane where i want the label move
double newMinX = grid.getChildren().get(1).getLayoutX();
double newMinY = grid.getChildren().get(1).getLayoutY();
Path path = new Path();
path.getElements().add(new MoveTo(oldMinX, oldMinY ));
path.getElements().add(new LineTo(newMinX,newMinY ));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(new Duration(500));
pathTransition.setPath(path);
pathTransition.setNode(label);
pathTransition.play();
}
If I change the arguments of MoveTo and LineTo by the following I can achieve the animation I want but I cant understand why. :\
double oldMinX = grid.getChildren().get(3).getLayoutX() -185;
double oldMinY = grid.getChildren().get(3).getLayoutY()+ grid.getChildren().get(3).getBoundsInLocal().getHeight()/2 ;
double newMinX = grid.getChildren().get(1).getLayoutX()-255 ;
double newMinY = grid.getChildren().get(1).getLayoutY() + grid.getChildren().get(0).getBoundsInLocal().getHeight()/2 ;
I guess It is because transitions use different coordinate systems than scenes but I cant really find anything that explains well :( Could someone give me some hints how It is working?
Thank you so much in advance.
I realized that i shouldn't use GridPane. If i do it without using containers the transition is working fine.

Adding borders to GridPane JavaFX

I am creating a board game in JavaFX using GridPane.
There are 7 different animations which could be placed in each grid (cell) of the grid.
Initially the grid looks like this
I tested adding a simple circle to it before programming my animation insertions. And it looks like this
The nodes added are SubScenes which include TimeLine animation. Each cell size is 40x40 and the SubScene size is also 40x40.
The subscenes when added, get on top of the gridpane border lines and it doesn't look good.
What can I do so that the nodes are added below the grid lines? i.e. the gridlines are on top of the nodes.
If it is not possible with GridPane, is there anything else I can use?
class which i execute for the game
class Game {
static GridPane grid;
public void start(final Stage stage) throws Exception {
int rows = 5;
int columns = 5;
stage.setTitle("Enjoy your game");
grid = new GridPane();
for(int i = 0; i < columns; i++) {
ColumnConstraints column = new ColumnConstraints(40);
grid.getColumnConstraints().add(column);
}
for(int i = 0; i < rows; i++) {
RowConstraints row = new RowConstraints(40);
grid.getRowConstraints().add(row);
}
grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
public void handle(MouseEvent me) {
grid.add(Anims.getAnim(1), (int)((me.getSceneX() - (me.getSceneX() % 40)) / 40), (int)((me.getSceneY() - (me.getSceneY() % 40)) / 40)); //here the getAnim argument could be between 1-7
}
});
grid.setStyle("-fx-background-color: white; -fx-grid-lines-visible: true");
Scene scene = new Scene(grid, (columns * 40) + 100, (rows * 40) + 100, Color.WHITE);
stage.setScene(scene);
stage.show();
}
public static void main(final String[] arguments) {
Application.launch(arguments);
}
}
class which contains animations, here I am just creating a circle
public class Anims {
public static SubScene getAnim(final int number) throws Exception {
Circle circle = new Circle(20, 20f, 7);
circle.setFill(Color.RED);
Group group = new Group();
group.getChildren().add(circle);
SubScene scene = new SubScene(group, 40, 40);
scene.setFill(Color.WHITE);
return scene;
}
}
Don't use setGridLinesVisible(true): the documentation explicitly states this is for debug only.
Instead, place a pane in all the grid cells (even the empty ones), and style the pane so you see the borders. (This gives you the opportunity to control the borders very carefully, so you can avoid double borders, etc.) Then add the content to each pane. You can also register the mouse listeners with the pane, which means you don't have to do the ugly math to figure out which cell was clicked.
The recommended way to apply a border to any region is to use CSS and a "nested background" approach. In this approach, you draw two (or more) background fills on the region, with different insets, giving the appearance of a border. So for example:
-fx-background-fill: black, white ;
-fx-background-insets: 0, 1 ;
will first draw a black background with no insets, and then over that will draw a white background with insets of 1 pixel on all sides, giving the appearance of a black border of width 1 pixel. While this may seem counter-intuitive, the performance of this is (allegedly) better than specifying border directly. You can also specify a sequence of four values for the insets for each fill, which are interpreted as the insets on the top, right, bottom, and left, respectively. So
-fx-background-fill: black, white ;
-fx-background-insets: 0, 0 1 1 0 ;
has the effect of a black border on the right and bottom, etc.
I'm also not sure SubScene is what you really want, unless you are intending attaching different cameras to each cell. If you really need a subscene, make the fill transparent to avoid drawing over the edges of the cell. You could just add the Group directly to each cell (you could probably just add the circle, depending on exactly what you need...).
Something like:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.RowConstraints;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class Game2 extends Application{
#Override
public void start(final Stage stage) throws Exception {
int rows = 5;
int columns = 5;
stage.setTitle("Enjoy your game");
GridPane grid = new GridPane();
grid.getStyleClass().add("game-grid");
for(int i = 0; i < columns; i++) {
ColumnConstraints column = new ColumnConstraints(40);
grid.getColumnConstraints().add(column);
}
for(int i = 0; i < rows; i++) {
RowConstraints row = new RowConstraints(40);
grid.getRowConstraints().add(row);
}
for (int i = 0; i < columns; i++) {
for (int j = 0; j < rows; j++) {
Pane pane = new Pane();
pane.setOnMouseReleased(e -> {
pane.getChildren().add(Anims.getAtoms(1));
});
pane.getStyleClass().add("game-grid-cell");
if (i == 0) {
pane.getStyleClass().add("first-column");
}
if (j == 0) {
pane.getStyleClass().add("first-row");
}
grid.add(pane, i, j);
}
}
Scene scene = new Scene(grid, (columns * 40) + 100, (rows * 40) + 100, Color.WHITE);
scene.getStylesheets().add("game.css");
stage.setScene(scene);
stage.show();
}
public static class Anims {
public static Node getAtoms(final int number) {
Circle circle = new Circle(20, 20f, 7);
circle.setFill(Color.RED);
Group group = new Group();
group.getChildren().add(circle);
// SubScene scene = new SubScene(group, 40, 40);
// scene.setFill(Color.TRANSPARENT);
return group;
}
}
public static void main(final String[] arguments) {
Application.launch(arguments);
}
}
and the css:
.game-grid {
-fx-background-color: white ;
-fx-padding: 10 ;
}
.game-grid-cell {
-fx-background-color: black, white ;
-fx-background-insets: 0, 0 1 1 0 ;
}
.game-grid-cell.first-row {
-fx-background-insets: 0, 1 1 1 0 ;
}
.game-grid-cell.first-column {
-fx-background-insets: 0, 0 1 1 1 ;
}
.game-grid-cell.first-row.first-column {
-fx-background-insets: 0, 1 ;
}
Simply add an H and V gap of one pixel width and let the grid pane's background color "shine" through:
.my-grid-pane {
-fx-background-color: lightgray;
-fx-vgap: 1;
-fx-hgap: 1;
-fx-padding: 1;
}
If the grid pane's background color spreads from outside more than one pixel (will happen if its parent is larger than itself), just wrap the grid in a Group!
I apologize for the response instead of the comment, not enough reputation.
Strangely, but #James_D 's response didn't help me; when the window was resized, the cell borders randomly changed their width, overlapping each other.
This answer helped solve the problem, so by slightly changing the code given by #James_D (only the .css file), we get:
.classes-grid {
-fx-background-color: white ;
-fx-padding: 10 ;
}
.classes-grid-cell {
-fx-border-color: dimgray;
-fx-border-width: 0 1 1 0;
-fx-background-color: transparent;
}
.classes-grid-cell.first-row {
-fx-border-width: 1 1 1 0 ;
}
.classes-grid-cell.first-column {
-fx-border-width: 0 1 1 1 ;
}
.classes-grid-cell.first-row.first-column {
-fx-border-width: 1 ;
}
Same idea with Mordechai's answer. But if you want to set these things by JavaFX code, not CSS stylesheet. Then you can do sth like this:
Set up the Hgap and Vgap: gridpane.setHgap(1) and gridpane.setVgap(1)
Set up the background color: gridpane.setBackground(new Background(new BackgroundFill(Color.rgb(0,0,0), new CornerRadii(2.5), new Insets(-1.0)))) (CornerRadii and Insets value depends on your choice, background color determined by rgb value)

Categories

Resources