https://stackoverflow.com/a/10615258/529411
I would like to add a background color to my tabpane dynamically (depending on certain conditions). How can I achieve this from code? One option is to assign he tab a specific ID which has the associated CSS, but in my case the color can be dynamically chosen by the user.
Also, I'm curious how to apply the styles in code when dealing with a hierarchy of components.
You can assign the background color to be a looked-up color in the CSS file:
.tab-pane > .tab-header-area > .tab-header-background {
-fx-background-color: -fx-outer-border, -fx-text-box-border, my-tab-header-background ;
}
Now in code you can set the value of the looked-up color whenever you need to:
tabPane.setStyle("my-tab-header-background: blue ;");
SSCCE:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.stage.Stage;
public class DynamicTabHeaderBackground extends Application {
private static final String TAB_HEADER_BACKGROUND_KEY = "my-tab-header-background" ;
#Override
public void start(Stage primaryStage) {
TabPane tabPane = new TabPane();
tabPane.setStyle(TAB_HEADER_BACKGROUND_KEY+": blue ;");
tabPane.getTabs().addAll(new Tab("Tab 1"), new Tab("Tab 2"));
tabPane.getSelectionModel().selectedIndexProperty().addListener((obs, oldIndex, newIndex) -> {
if (newIndex.intValue() == 0) {
tabPane.setStyle(TAB_HEADER_BACKGROUND_KEY+": blue ;");
} else {
tabPane.setStyle(TAB_HEADER_BACKGROUND_KEY+": green ;");
}
});
Scene scene = new Scene(tabPane, 400, 400);
scene.getStylesheets().add("dynamic-tab-header.css");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
with dynamic-tab-header.css containing the CSS code above.
Update
If you have multiple tab panes, you might want to consider the following variant of the CSS file:
.tab-pane {
my-tab-header-background: derive(-fx-text-box-border, 30%) ;
}
.tab-pane > .tab-header-area > .tab-header-background {
-fx-background-color: -fx-outer-border, -fx-text-box-border,
linear-gradient(from 0px 0px to 0px 5px, -fx-text-box-border, my-tab-header-background) ;
}
This basically emulates the default behavior, but allows you to modify the background on any particular tab pane by calling the tabPane.setStyle(...) code as before.
Related
I am using an JavaFX Alert with a text area on it.
The problem I have is that the text area does not use the full space of the Alert, as well as having white (borders).
My code:
TextArea area = new TextArea("");
area.setWrapText(true);
area.setEditable(false);
area.getStylesheets().add(getClass().getResource("/model/app.css").toExternalForm());
Alert alert = new Alert(AlertType.NONE);
alert.getDialogPane().setPrefWidth(750);
alert.getDialogPane().setPrefHeight(800);
alert.getDialogPane().setContent(area);
formatDialog(alert.getDialogPane());
alert.setTitle("Lizenz Info");
Window w = alert.getDialogPane().getScene().getWindow();
w.setOnCloseRequest(e -> {
alert.hide();
});
w.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (event.getCode() == KeyCode.ESCAPE) {
w.hide();
}
}
});
alert.setResizable(true);
alert.showAndWait();
My corresponding css sheet:
.text-area .content {
-fx-background-color: #4c4c4c;
}
.text-area {
-fx-text-fill: #ff8800;
-fx-font-size: 15.0px;
}
.text-area .scroll-pane {
-fx-background-color: #4c4c4c;
}
.text-area .scroll-pane .viewport {
-fx-background-color: #4c4c4c;
}
.text-area .scroll-pane .content {
-fx-background-color: #4c4c4c;
}
.viewport and .content on .scrollpane did not have any effect whatsoever.
I want the white borders either to be gone, or have the same color as the background, also to use the full space of the dialog. Can someone help?
As #jewelsea suggested, I think Alert is not the right choice here. Your desired layout can be acheived by using Dialog (as in below code).
Dialog<String> dialog = new Dialog<>();
dialog.setTitle("Lizenz Info");
dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK);
dialog.getDialogPane().setContent(area);
dialog.setResizable(true);
dialog.showAndWait();
Having said that, you can fix the existing issues as below:
Remove white space around text area: You can remove the white space by setting the padding of TextArea to 0. Include the below code in the css file.
.text-area{
-fx-padding:0px;
}
Changing the white space background : The .text-area and .content styleclasses are on same node. So instead of declaring with space between them
.text-area .content {
-fx-background-color: #4c4c4c;
}
you have to declare without the space between the styleclasses (in below code)
.text-area.content {
-fx-background-color: #4c4c4c;
}
Here is a similar example to Sai's but uses a standard stage.
It uses a UTILITY style, but you could use a different style if you prefer.
Basically, if you don't want the additional styling and functionality of the alerts and dialogs (and you don't seem to with at least the example you have given), then you can just use a standard stage to display your content rather than the dialog classes provided in the javafx.control package.
The alert.css file referenced in the example is the CSS from your question.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.*;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class TextAreaUtility extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) {
Button showAlert = new Button("Show Alert");
showAlert.setOnAction(this::showAlert);
stage.setScene(new Scene(showAlert));
stage.show();
}
private void showAlert(ActionEvent e) {
TextArea textArea = new TextArea("");
textArea.setWrapText(true);
textArea.setEditable(false);
Scene scene = new Scene(textArea, 750, 800);
scene.getStylesheets().add(
TextAreaUtility.class.getResource(
"alert.css"
).toExternalForm()
);
Stage utility = new Stage(StageStyle.UTILITY);
utility.initOwner(((Button) e.getSource()).getScene().getWindow());
utility.initModality(Modality.APPLICATION_MODAL);
utility.setTitle("Alert Title");
utility.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode() == KeyCode.ESCAPE) {
utility.hide();
}
});
utility.setResizable(true);
utility.setScene(scene);
utility.showAndWait();
}
}
Debugging nodes and styles info
If you want to see the nodes and style names in your scene graph and you aren't using a tool like ScenicView, a quick debug function is:
private void logChildren(Node n, int lvl) {
for (int i = 0; i < lvl; i++) {
System.out.print(" ");
}
System.out.println(n + ", " + n.getLayoutBounds());
if (n instanceof Parent) {
for (Node c: ((Parent) n).getChildrenUnmodifiable()) {
logChildren(c, lvl+1);
}
}
}
Which you can attach to run when the window is displayed:
w.setOnShown(se -> logChildren(alert.getDialogPane().getScene().getRoot(), 0));
When you run this on a standard dialog you will see quite a few nodes in the scene graph with attached styles that you can find defined in the modena.css file within the JavaFX SDK. You will also see that some of the bounding boxes for the layout that are not related to your text area have width and height.
Those dialog styles by default have padding attached to them, which is why you are seeing padding around your TextArea. The padding is not in the text area but the content regions containing it within the dialog. To get rid of it, you need to set the padding in your custom CSS to override the default. I don't have the CSS for that, it is difficult to create sometimes and overriding default padding is probably best avoided when possible.
I want to have transparent progressindicator, which is indefinite.
here is the code, it shows grey background state/scene.
i wanted fully transparent.
I tried following code, but it shows background stage which is not transparent.
package application;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
#Override
public void start(Stage stage) {
/*
*
* my css file content:
*
* .progress-indicator .indicator { -fx-background-color: transparent;
* -fx-background-insets: 0; -fx-background-radius: 0;
*
* } .progress-indicator { -fx-progress-color: green ; }
*
*
*
*/
Stage initStage = new Stage();
initStage.initStyle(StageStyle.TRANSPARENT);
ProgressIndicator loadProgress = new ProgressIndicator();
loadProgress.setSkin(null);
loadProgress.setPrefWidth(50);
VBox box = new VBox();
box.getChildren().add(loadProgress);
final Scene scene = new Scene(box, 150, 150);
scene.setFill(Color.TRANSPARENT);
initStage.setScene(scene);
scene.getStylesheets().add("application.css");
initStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
For modena.css (the default JavaFX look and feel definition in Java 8), a slight shaded background was introduced for all controls (and also to panes if a control is loaded).
You can remove this by specifying that the default background is transparent. This can be done by adding the following line to your application's CSS file:
.root { -fx-background-color: transparent; }
This is in addition to other settings you already have in your code to initialize the style of the stage and background fill of the scene.
stage.initStyle(StageStyle.TRANSPARENT);
scene.setFill(Color.TRANSPARENT);
Note: in the questions's sample code, an additional stage (initStage) is created instead of using the passed in stage for the start method. The passed in stage can be initialized, utilized and shown directly by your code rather than creating an additional initStage.
stage.initStyle(StageStyle.TRANSPARENT);
this is for hide the top bar ( minimize, Restore Down and close)
scene.setFill(Color.TRANSPARENT);
this is for the frame color ( you can replace TRANSPARENT with any color GREEN YELLOW RED BLUE ...) but for me I want glass view if you can understand me, and with different color so the solution is
primaryStage.setOpacity(0.2);
The number 0.2 is between 0 and 1. 0 is hidden and 1 is normal form but between the numbers transparent so choose your number and run your program and see if this is what you want there is this code for full screen.
primaryStage.setFullScreen(true);
and in the css file do this
.root { -fx-background-color:rgba(0,0,0,1); }
and you can change the color with changed the number in rgba(0,0,0,1)
This works for me.
Parent root = FXMLLoader.load(getClass().getResource("login.fxml"));
Scene scene = new Scene(root);
scene.setFill(Color.TRANSPARENT);
stage.setScene(scene);
stage.initStyle(StageStyle.TRANSPARENT);
stage.show();
U just need mainly 2 things:
scene.setFill(Color.TRANSPARENT);
stage.initStyle(StageStyle.TRANSPARENT);
I am trying to build a Next/Previous windows using TabPane. I decided to use TabPane as it is easy to use and design in SceneBuilder. At the start fo the app, I used this to hide the TabBar for now-
tabPane.setTabMinHeight(-10);
tabPane.setTabMaxHeight(-10);
The appearance of the TabPane after this-
As you can see, there still remains a small part of TabBar (below the titlebar). How can I hide it completely so that my TabPane will look like just a normal Pane but with all its functionality intact?
Using a TabPane with hidden tabs as a wizard-type interface is an interesting idea, which I hadn't thought of and think I like.
You can hide the tabs with the following in an external CSS file:
.tab-pane {
-fx-tab-max-height: 0 ;
}
.tab-pane .tab-header-area {
visibility: hidden ;
}
Here's a SSCCE. In this I gave the tab pane the CSS class wizard.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class TabPaneAsWizard extends Application {
#Override
public void start(Stage primaryStage) {
TabPane tabPane = new TabPane();
tabPane.getStyleClass().add("wizard");
for (int i = 1; i<=10; i++) {
tabPane.getTabs().add(createTab(i));
}
Button previous = new Button("Previous");
previous.setOnAction(e ->
tabPane.getSelectionModel().select(tabPane.getSelectionModel().getSelectedIndex()-1));
previous.disableProperty().bind(tabPane.getSelectionModel().selectedIndexProperty().lessThanOrEqualTo(0));
Button next = new Button("Next");
next.setOnAction(e ->
tabPane.getSelectionModel().select(tabPane.getSelectionModel().getSelectedIndex()+1));
next.disableProperty().bind(
tabPane.getSelectionModel().selectedIndexProperty().greaterThanOrEqualTo(
Bindings.size(tabPane.getTabs()).subtract(1)));
HBox buttons = new HBox(20, previous, next);
buttons.setAlignment(Pos.CENTER);
BorderPane root = new BorderPane(tabPane, null, null, buttons, null);
Scene scene = new Scene(root, 600, 600);
scene.getStylesheets().add("tab-pane-as-wizard.css");
primaryStage.setScene(scene);
primaryStage.show();
}
private Tab createTab(int id) {
Tab tab = new Tab();
Label label = new Label("This is step "+id);
tab.setContent(label);
return tab ;
}
public static void main(String[] args) {
launch(args);
}
}
tab-pane-as-wizard.css:
.wizard {
-fx-tab-max-height: 0 ;
}
.wizard .tab-header-area {
visibility: hidden ;
}
The easy way of doing this is changing the colour to synchronize it with background
.tab-pane {
-fx-tab-max-height: 0;
}
.tab-pane .tab-header-area .tab-header-background {
-fx-background-color: #843487;//your background colour code
}
.tab-pane .tab
{
-fx-background-color: #843487;//your background colour code
}
Just a little correction to your answers :
.tab-pane {
-fx-tab-max-height: 0 ;
}
.tab-pane .tab-header-area {
visibility: hidden ;
-fx-padding: -20 0 0 0;
}
I want to change background and border color of TextField use JavaFX CSS. I don't understand why -fx-border-color reset border radius of TextField?
As you can see the second TextField doesn't have border radius.
sample/style.css:
.validation-error {
-fx-background-color: #FFF0F0;
-fx-border-color: #DBB1B1;
}
sample/Main.java
package sample;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
TextField txtWithoutStyle = new TextField();
txtWithoutStyle.setText("Without Style");
TextField txtWithStyle = new TextField();
txtWithStyle.setText("With Style");
txtWithStyle.getStyleClass().add("validation-error");
VBox root = new VBox();
root.setPadding(new Insets(14));
root.setSpacing(14);
root.getChildren().addAll(txtWithoutStyle, txtWithStyle);
root.getStylesheets().add("/sample/style.css");
Scene scene = new Scene(root, 300, 275);
primaryStage.setTitle("Hello World");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Update 1
Additional question: Why -fx-background-color remove TextField border (just remove -fx-border-color from style.css to reproduce it)?
The default stylesheet applies borders to text fields (and almost all other controls) by using "nested backgrounds" instead of borders.
Some of the settings for the TextInputControl from the default stylesheet are:
-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;
This basically sets two background colors (both defined by a linear gradient), one (the "outer" one) with a color based on -fx-text-box-border, and the other with a color based on -fx-control-inner-background. The "outer" background is outside the "inner" background because they have insets of 0 and 1, respectively; the curved edge to the resulting apparent border is created by having radii of 3 and 2 for each background, respectively.
This is, anecdotally at least, far more efficient than using actual borders in the CSS, so this choice of technique is for performance reasons.
So to preserve the radius for the border, you can use the same technique, and just override the two background colors:
.validation-error {
-fx-background-color: #DBB1B1, #FFF0F0 ;
}
Note that you can also just replace the "looked-up-colors", which would also preserve the subtle linear gradients being used:
.validation-error {
-fx-text-box-border: #DBB1B1 ;
-fx-control-inner-background: #FFF0F0 ;
}
For highlighting when focused, the default uses colors named -fx-focus-color and -fx-faint-focus-color: so in the latter version you would probably want to redefine those too:
.validation-error {
-fx-text-box-border: #DBB1B1 ;
-fx-control-inner-background: #FFF0F0 ;
-fx-focus-color: #FF2020 ;
-fx-faint-focus-color: #FF202020 ;
}
I've created a very simple CSS that styles two buttons.
To the first has just been added a padding.
To the second has been set the -fx-background-color, but the value is taken from caspian.css, that is the value it should have before it had been set.
.first-style { -fx-padding: 20 5 1 5; }
.second-style { -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color; }
At this point i experience a strange behavior: the focus decoration stops working, and the second button doesn't get its blue border when focused.
What's happening?
You need to add a :focused psuedo-class to the second style to allow the focus ring to work otherwise you just overwrite it when you respecify the background color of the button in the second-style style class.
Sample CSS:
.root { -fx-background-color: cornsilk; -fx-padding: 10; }
.first-style { -fx-padding: 20 5 1 5; }
.second-style { -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color; }
.second-style:focused { -fx-background-color: -fx-focus-color, -fx-outer-border, -fx-inner-border, -fx-body-color; }
Sample app:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class ButtonFocusCss extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) throws Exception {
VBox layout = new VBox(15);
Button b1 = new Button("B1");
b1.getStyleClass().add("first-style");
Button b2 = new Button("B2");
b2.getStyleClass().add("second-style");
layout.getChildren().addAll(b1, b2);
layout.getStylesheets().add(getClass().getResource("button.css").toExternalForm());
stage.setScene(new Scene(layout));
stage.show();
}
}
Update
Honestly I can't explain exactly why the JavaFX CSS override mechanism works this way, I got the answer here by reviewing the default JavaFX 2.2 caspian.css and following a hunch on how it might work.
The best current explanation of the rules of application for JavaFX CSS is in the CSS Reference Guide section CSS and the JavaFX Scene Graph, though there are subtleties in this example for which you need to turn to a general CSS specification to understand things such as cascading order and specifity.