I am struggling and will no doubt have to buy a manual to understand JavaFX CSS, or the JavaFX CSS Reference Guide...
But what I want to do is make a 1 pixel border around some of my nodes, such as a TableView or ScrollPane on one side, and a GridPane or ScrollPane on the other side of a Scene I'm working on. I say "or", because I'll take either one. Ha ha!
(And regardless of whether it's filled up with controls or not)
Cheers.
Here's a test sample for demoing some ways to make borders in CSS:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class TableViewBorderTest extends Application {
#Override
public void start(Stage primaryStage) {
HBox root = new HBox(5);
TableView<String> table = new TableView<>();
table.getColumns().add(new TableColumn<String, String>("Data"));
ScrollPane scroller = new ScrollPane();
scroller.setMinWidth(200);
GridPane grid = new GridPane();
grid.setMinWidth(200);
grid.getStyleClass().add("grid");
root.getChildren().addAll(table, scroller, grid);
// padding so we can easily see borders:
root.setPadding(new Insets(10));
Scene scene = new Scene(root);
scene.getStylesheets().add("border-table.css");
primaryStage.setScene(scene);
primaryStage.show();
// remove focus from tables
root.requestFocus();
}
public static void main(String[] args) {
launch(args);
}
}
You can run this with just an empty border-table.css file. The first thing to notice is that the table view and scroll pane already have a 1-pixel border in a medium gray (slightly darker than the default background):
This border is defined (see how later) in the default stylesheet, modena.css, and is set to a "looked-up color" called -fx-box-border. Just for demo purposes, to make it easier to see the border, you can reassign this color with the following in border-table.css:
.root {
-fx-box-border: red ;
}
This gives
Notice that the table header and column headers use the same color as a border. If you compare to the first image, you can probably see the borders more clearly in it too.
To replace the border in the table view and scroll pane, you can define borders in the css file. The simplest, but not necessarily the best, way is to define a -fx-border-color. Replace the border-table.css with the following:
.table-view, .scroll-pane {
-fx-border-color: green ;
}
The default value of -fx-border-width is 1, so this gives a one-pixel green border:
For the GridPane, note that it has no default border and also has no style class (see CSS docs). In the Java code, I defined a style class for it:
grid.getStyleClass().add("grid");
so we can add the same border just by adding this style class to the selector:
.table-view, .scroll-pane, .grid {
-fx-border-color: green ;
}
It's interesting to note that the default stylesheet, modena.css doesn't use -fx-border-... properties at all. Instead, it creates borders by creating two (or more) "nested backgrounds". For example, it has:
.scroll-pane,
.split-pane,
.list-view,
.tree-view,
.table-view,
.tree-table-view,
.html-editor {
-fx-background-color: -fx-box-border, -fx-control-inner-background;
-fx-background-insets: 0, 1;
-fx-padding: 1;
}
This defines, for TableView, ScrollPane, and other similar controls, two background colors. The first (so painted first, i.e. underneath) is a solid background fill in the looked-up color -fx-box-border, and the second (painted on top) is a solid background fill in the looked-up color -fx-control-inner-background. The first background fill has 0 insets, and the second has a 1-pixel inset, meaning that the bottom background fill will be visible for 1 pixel width around the edge of the control. (The padding ensures nothing is placed over this effective 1-pixel border.)
I haven't tested this at all, but it's claimed that the nested background approach is more efficient than drawing borders (I guess the native graphics is vey fast at rectangular background fills).
So you could use the same approach and replace border-table.css with
.table-view, .scroll-pane {
-fx-background-color: blue, -fx-control-inner-background ;
-fx-background-insets: 0, 1 ;
}
.grid {
-fx-background-color: blue, -fx-background ;
-fx-background-insets: 0, 1 ;
-fx-padding : 1 ;
}
And you could even introduce a looked-up color to make it easier to modify the style of the app:
.root {
-my-border: blue ;
}
.table-view, .scroll-pane {
-fx-background-color: -my-border, -fx-control-inner-background ;
-fx-background-insets: 0, 1 ;
}
.grid {
-fx-background-color: -my-border, -fx-background ;
-fx-background-insets: 0, 1 ;
-fx-padding : 1 ;
}
(this has exactly the same effect as the previous, but there is just one place to change the color definition instead of two).
Note these last two versions override the default focus border, which is implemented in the default style sheet by defining a different set of background colors when the controls are focused. You can restore these with:
.root {
-my-border: blue ;
}
.table-view, .scroll-pane {
-fx-background-color: -my-border, -fx-control-inner-background ;
-fx-background-insets: 0, 1 ;
}
.table-view:focused, .scroll-pane:focused {
-fx-background-color: -fx-faint-focus-color, -fx-focus-color, -fx-control-inner-background;
-fx-background-insets: -1.4, -0.3, 1;
-fx-background-radius: 2, 0, 0;
}
.grid {
-fx-background-color: -my-border, -fx-background ;
-fx-background-insets: 0, 1 ;
-fx-padding : 1 ;
}
which references two more looked-up colors, -fx-faint-focus-color and -fx-focus-color (the first is just a partially-transparent version of the second); of course you could redefine these for your own focus colors if you chose.
Related
Can someone please tell me why -fx-text-fill in the below example doesn't work to change the font color? -fx-stroke is something I've tried as well. Thanks!
Java File:
package SimpleTextFromCSS;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class Main extends Application
{
#Override
public void start(Stage primaryStage)
{
GridPane rootGP = new GridPane();
Label centerText = new Label("Sample text.");
rootGP.getChildren().add(centerText);
Scene scene = new Scene(rootGP,1200,800);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.setScene(mainScene);
primaryStage.show();
}
}
public static void main(String[] args)
{
launch(args);
}
}
CSS:
.root{
-fx-font-family: "Broadway";
-fx-font-size: 50pt;
-fx-text-fill: blue;
}
As pointed out in the comments (thanks to #Slaw), the -fx-text-fill property is not inherited by default, so setting it for your root pane will not allow it to propagate down to the labels in your UI. A possible fix would be to define the property for, say .label, but a better approach is to hook into the "theming" functionality of the default style sheet ("modena"). Admittedly, the documentation on this is pretty thin, but reading the comments in the CSS source code, particularly at the top of the file, indicate how this is intended to work.
Modena defines a "looked-up color" called -fx-text-background-color, which is used for text painted on top of a background filled with -fx-background-color. A Label is such a text, and the default style sheet sets the text fill of a label to -fx-text-background-color.
So one approach is to redefine the -fx-text-background-color value:
.root{
-fx-font-family: "Broadway";
-fx-font-size: 50pt;
-fx-text-background-color: blue;
}
If you want to be a little more sophisticated, the default value of -fx-text-background-color is actually a "ladder", which picks a color depending on the intensity of the background. It is itself defined in terms of three other colors:
-fx-light-text-color (for dark backgrounds, which defaults to white),
-fx-mid-text-color (for light backgrounds, which defaults to a light grey), and
-fx-dark-text-color (for medium backgrounds, which defaults to black).
So if you want a solution that would be visually robust to changes in the background of a label, you could do
.root{
-fx-font-family: "Broadway";
-fx-font-size: 50pt;
-fx-dark-text-color: navy ;
-fx-mid-text-color: blue ;
-fx-light-text-color: skyblue ;
}
ListView listView = new ListView<>();
//something like
listview.removeBorder or listview.setborder(Empty border)??
Well, it depends on the theme you are using in your application.
In Modena (default JavaFx 8+ theme), ListView borders and background are implemented as a background layers, and each layer is just a plain color fill:
.list-view {
-fx-background-color: -fx-box-border, -fx-control-inner-background; //this line
-fx-background-insets: 0, 1;
-fx-padding: 1;
}
So, to remove borders you need to remove the first color fill (-fx-box-border) and keep the second one (-fx-control-inner-background, which is color constant with a value #F4F4F4 and represents ListView background color):
listView.setBackground(
new Background(new BackgroundFill(Color.valueOf("F4F4F4"), null, null))
);
and you'll probably want to remove that 1px padding that was used for borders:
listView.setPadding(new Insets(0));
I have a toggle button in my program that starts/stops a script. I would like for this button to be green and say "START" when the button is not selected, and red and say "STOP" when it is selected. More importantly, I would like the unselected hover color to be a slightly darker version of the original green, and the selected hover color to be a slightly darker version of the red color. My current CSS for this button looks like this:
#startStopButton {
-fx-border-color:#d4d4d4;
-fx-background-color:#85eca5;
-fx-background-image: url("startButton.png");
-fx-background-size: 50px;
-fx-background-repeat: no-repeat;
-fx-background-position: 80% 50%;
-fx-alignment: CENTER_LEFT;
-fx-effect: dropshadow(three-pass-box, #e7e7e7, 15, 0, 0, 0);
}
#startStopButton:hover {
-fx-background-color:#80dc9c;
}
#startStopButton:selected{
-fx-background-color: #ff6060;
-fx-text:"STOP";
}
#startStopButton:selected:focused{
-fx-background-color: #ff6060;
-fx-text:"STOP";
}
Currently, this will work fine, except for when the button turns red. In this case, there is no hover effect. Within my FXML controller, there is a function that is activated every time this button is clicked:
private void startStopClick()
{
if(startStopButton.isSelected())
{
startStopButton.setText(" STOP");
// startStopButton.setStyle()
}
else {
startStopButton.setText(" START");
}
}
Is there any way to 1) set the button text within CSS so that I can leave that out of my controller?
2) Get the current toggle button state in CSS, so that I can have multiple hover effects. For example, something like this:
#startStopButton:unselected{
-fx-background-color: #ff6060;
-fx-text:"STOP";
}
If there is no way to do this in CSS, can I set the hover styles in the Java code in the FXML controller?
CSS properties are only available for the look of nodes. With a few exceptions the basic JavaFX nodes don't allow you to specify content via CSS. The text property of buttons is no exception; it cannot be set using CSS.
As for the colors: The rules occuring last override values assigned by rules with the same precedence occuring before them. This means the background color assigned by the rules for #startStopButton:selected and #startStopButton:selected:focused always override the color #startStopButton:hover assigns.
Since in both cases you want a darker color when hovering, the derive function and a lookedup color may work for you.
Example
#Override
public void start(Stage primaryStage) {
ToggleButton btn = new ToggleButton();
btn.getStyleClass().add("start-stop");
btn.textProperty().bind(Bindings.when(btn.selectedProperty()).then(" STOP").otherwise(" START"));
Pane p = new Pane(btn);
Scene scene = new Scene(p);
scene.getStylesheets().add("style.css");
primaryStage.setScene(scene);
primaryStage.show();
}
style.css
.start-stop.toggle-button {
base-color: #85eca5;
-fx-background-color: base-color;
}
.start-stop.toggle-button:selected {
base-color: #ff6060;
}
.start-stop.toggle-button:hover {
-fx-background-color: derive(base-color, -20%);
}
If you cannot use derive since you need to specify different colors for all 4 states you could still rely on looked-up colors to avoid relying on the rule ordering:
.start-stop.toggle-button {
unselected-color: blue;
selected-color: yellow;
-fx-background-color: unselected-color;
}
.start-stop.toggle-button:hover {
unselected-color: red;
selected-color: green;
}
.start-stop.toggle-button:selected {
-fx-background-color: selected-color;
}
I am trying to change the background color of my TextField "colorBox0" to "value0" but it gets rid of the border.
Here is a simplified version of my code:
static Paint value0 = Paint.valueOf("FFFFFF");
TextField colorBox0;
colorBox0.setBackground(new Background(new BackgroundFill(value0, CornerRadii.EMPTY, Insets.EMPTY)));
Any help is very much appreciated
Thank you
I found that you can construct a string of css code out of a string and a variable by using the to string method and the substring method like this:
colorBox0
.setStyle("-fx-control-inner-background: #"+value0.toString().substring(2));
Looking at the (shortened) default JavaFX styles for the TextField explains a lot:
.text-input {
-fx-background-color: linear-gradient(to bottom, derive(-fx-text-box-border, -10%), -fx-text-box-border),
linear-gradient(from 0px 0px to 0px 5px, derive(-fx-control-inner-background, -9%), -fx-control-inner-background);
-fx-background-insets: 0, 1;
-fx-background-radius: 3, 2;
}
So the background is a layered background including the border. This technique is used a lot throughout JavaFX. But it is very easy to modify just one color.
First we need to assign a new custom style class to our TextField:
TextField textField = new TextField();
textField.getStyleClass().add("custom");
and the CSS file:
.custom {
-fx-control-inner-background: orange;
}
As you can see, you do not have to override all styles of the textfield, it is sufficient to only override the color variable used for the background.
Try to set the color using CSS:
TextField colorBox0;
colorBox0.setStyle("-fx-background-color: white;");
Elegant solution with colour translation:
static Paint black = Paint.valueOf(Integer.toHexString(Color.BLACK.hashCode()));
TextField textfield;
textField.setStyle(
"-fx-control-inner-background: #"+black.toString().substring(2));
I want to create buttons like this in JavaFX (not html).
http://jsfiddle.net/x7dRU/3/
(hover on them to see the effect)
[Stupid Stackoverflow insists on me posting jsfiddle code here which isn't relevant]
<li>Button 1</li>
So with a rounded border and a transparent background. Unfortunately the background/insets technique seems to overwrite the content from outside to in. So if you draw a bright border, then you can't undo the brightness to create a dark&transparent background without hardcoding the colour. I.e. it's not write once, run everywhere, on different coloured panels.
-fx-border-color doesn't seem to support rounding or at least isn't recommended here Set border size . I imagine the rounding of the border doesn't sync with the rounding of the background.
Seems HTML5 has the edge on this one then. Tell me I'm wrong :-) ...although I suspect my question can't be done without specifying the colour for each and every button context.
Browny points.
Note, I realise I've coloured the white border greenish (context sensitive), I'm happy with a border of semi-transparent white as a solution. First prize would be a burn/dodge/etc(background-colour) function ala photoshop.
Plan B.
It doesn't look so bad without rounded edges, so maybe I should just resort to -fx-border-color
Background info
Have a look at the information in the css documentation on "looked-up colors"(scroll down a little, beyond the named color section).
The way these basically work, is that you can define a "looked up color" (i.e. a color-valued variable) and apply it to a node in the scene graph. The value is inherited by any node descended from that node in the scene graph.
Now have a browse through the default style sheet, modena.css. The way this works is that almost everything is defined in terms of a very short list of looked-up colors. The consequence is that you can readily "theme" your applications just by redefining those colors on the root of the scene. Try adding this stylesheet to your favorite JavaFX application:
.root {
-fx-base: #c7dec7;
-fx-accent: #00c996 ;
-fx-default-button: #abedd8 ;
-fx-focus-color: #03d39e;
-fx-faint-focus-color: #03d39e22;
}
As you've observed, -fx-border is not used at all in the default stylesheet; instead borders are implemented by defining "nested" background colors which are essentially rectangular fills laid on top of each other. This is apparently more efficient (quite considerably so, by my understanding). So you are correct that making the inner of two backgrounds transparent will simply reveal the "outer" border color, not the color of the background.
How to achieve what you're looking for
The background of a pane defaults to the looked-up color -fx-background, which in turn defaults to a lighter version of -fx-base. So if you stick to changing the color of the pane containing the buttons by changing -fx-background or -fx-base, then you can make the button appear transparent by setting its background to
-fx-background-color: (some-border-color), -fx-background ;
The default borders for buttons contain three values; -fx-shadow-highlight, -fx-outer-border, and -fx-inner-border. You could override the values for these individually, or just redefine the background color as you need.
An approximation to what you want is in this example: you can mess with the exact values for the thickness of the border (from the second -fx-background-insets value) and the radius of the corners to get it as you need. If you want to get fancy with it, play with combinations of ladders and gradients.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class StyledButtonExample extends Application {
private int currentColorIndex = 0 ;
private final String[] baseColors = new String[] {"#8ec919", "#bfe7ff", "#e6e6fa",
"#ffcfaf", "#fff7f7", "#3f474f"};
private StackPane root ;
#Override
public void start(Stage primaryStage) {
root = new StackPane();
Button button = new Button("Change colors");
button.setOnAction(event -> changeColors());
root.getChildren().add(button);
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("transparent-button.css").toExternalForm());
root.setStyle("-fx-base: "+baseColors[0]+";");
primaryStage.setScene(scene);
primaryStage.show();
}
private void changeColors() {
currentColorIndex = (currentColorIndex + 1) % baseColors.length ;
root.setStyle("-fx-base: "+baseColors[currentColorIndex]+";");
}
public static void main(String[] args) {
launch(args);
}
}
transparent-button.css:
.button {
-fx-background-color: derive(-fx-base, 60%), -fx-background ;
-fx-background-insets: 0, 1px ;
-fx-background-radius: 4px, 0 ;
}
.button:hover {
-fx-background-color: #fff, derive(-fx-background, -5%) ;
}