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;
}
Related
Is there a way (CSS or Java) to style first and last tab only in a dynamic TabPane?
Example :
Thanks!
You can observe the ObservableList<Tab> returned by TabPane#getTabs() and update the style class of each Tab as appropriate. For example:
App.java:
import javafx.application.Application;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.css.Styleable;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TabPane.TabClosingPolicy;
import javafx.scene.control.TabPane.TabDragPolicy;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage primaryStage) {
TabPane pane = new TabPane();
pane.setTabClosingPolicy(TabClosingPolicy.ALL_TABS);
pane.setTabDragPolicy(TabDragPolicy.REORDER); // requires JavaFX 10+
pane.getTabs().addListener(App::tabsChanged);
pane.getTabs() // add tabs **after** adding ListChangeListener
.addAll(
new Tab("Test Tab #1", new StackPane(new Label("Content #1"))),
new Tab("Test Tab #2", new StackPane(new Label("Content #2"))),
new Tab("Test Tab #3", new StackPane(new Label("Content #3"))),
new Tab("Test Tab #4", new StackPane(new Label("Content #4"))),
new Tab("Test Tab #5", new StackPane(new Label("Content #5"))));
Scene scene = new Scene(pane, 600, 400);
scene.getStylesheets().add(getClass().getResource("/App.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
private static void tabsChanged(Change<? extends Tab> c) {
while (c.next()) {
if (c.wasRemoved()) {
for (Tab removed : c.getRemoved()) {
removed.getStyleClass().removeAll("first-tab", "last-tab");
}
}
}
ObservableList<? extends Tab> tabs = c.getList();
if (tabs.size() == 1) {
Tab tab = tabs.get(0);
addStyleClassIfAbsent(tab, "first-tab");
addStyleClassIfAbsent(tab, "last-tab");
} else if (!tabs.isEmpty()) {
Tab first = tabs.get(0);
addStyleClassIfAbsent(first, "first-tab");
first.getStyleClass().remove("last-tab");
Tab last = tabs.get(tabs.size() - 1);
addStyleClassIfAbsent(last, "last-tab");
last.getStyleClass().remove("first-tab");
for (Tab middle : tabs.subList(1, tabs.size() - 1)) {
middle.getStyleClass().removeAll("first-tab", "last-tab");
}
}
}
private static void addStyleClassIfAbsent(Styleable styleable, String styleClass) {
ObservableList<String> styleClasses = styleable.getStyleClass();
if (!styleClasses.contains(styleClass)) {
styleClasses.add(styleClass);
}
}
}
App.css:
.first-tab,
.last-tab {
-fx-base: pink;
}
The -fx-base is a looked-up color added by modena.css (i.e. the default user-agent stylesheet in JavaFX 8+). I set that instead of the -fx-background-color property in order to hook into the "theming" provided by modena.css. In the above example you can see the styles change dynamically by reordering the tabs via mouse-dragging (JavaFX 10+) or by closing tabs.
Note I would have preferred to use PseudoClass for this. However, from what I can tell, the Tab class does not allow you to (de)activate pseudo-classes directly. The way it handles the :selected pseudo-class is internal to the TabPane's default skin, meaning we can't access that same functionality reliably for our purposes from the outside.
I'm wondering if there is a way to apply some transformations (i.e. rotate) to an image setted to some button. I am using css to specify all images by such way:
.custom-button {
-fx-graphic: url("imgs/buttons/button.png");
...
}
.custom-button:hover {
-fx-graphic: url("imgs/buttons/button_hover.png");
...
}
.custom-button:selected {
-fx-graphic: url("imgs/buttons/button_selected.png");
...
}
I want to specify such transformation here in css as well.
How can I achieve that? I am supposing to find something like:
.custom-button .graphic {
-fx-rotate: 90;
}
Let's start with an example application:
Main.java
package application;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
Button button = new Button("Button");
VBox vBox = new VBox(button);
vBox.setPadding(new Insets(10.0));
Scene scene = new Scene(vBox, 200, 100);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
System.out.println();
}
public static void main(String[] args) {
launch(args);
}
}
application.css
.button {
-fx-graphic: url(image.png);
}
Result
Method 1 (find out which class is used for the image)
This can be easily done using a debugger (set a breakpoint on println() and check the content of button.graphic.value). The class which is used here is ImageView. This means the image can be rotated using:
.button .image-view {
-fx-rotate: 45;
}
Result
Method 2 (set a custom class for the graphic object)
This can be done using a ChangeListener:
button.graphicProperty().addListener((ChangeListener<Node>) (observable, oldValue, newValue) -> {
newValue.getStyleClass().add("my-class");
});
Then the following can be used to rotate the image:
.my-class {
-fx-rotate: 45;
}
Result
Padding
You might need to add additional padding to the button, if the image takes up too much space:
.button {
-fx-graphic: url(image.png);
-fx-graphic-text-gap: 10;
-fx-label-padding: 5 0 5 5;
}
Result
From JavaFX CSS I want to apply an effect only to the prompt-text without affecting the text in a TextField but do not know how to access that item. I can only change the color with -fx-prompt-text-fill. When I apply an effect to the text the prompt-text it is also affected, why?
.text-field {
-fx-prompt-text-fill: gray;
}
.text-field > .text {
-fx-effect: dropshadow( two-pass-box , blue , .5, 10 , 1 , 1);
}
In the above code also applies shadow to prompt-text what I want to avoid !!
You can style the Prompt text with a stylesheet if you use JFoenix. The CSS-Class is .jfx-text-field and the property -fx-prompt-text-fill.
Example:
.jfx-text-field {
-fx-prompt-text-fill: #989898;
}
If you need other components, look it up: JFoenix GitHub components page
The prompt text only shows when the text field is empty, so the easiest way I can see to do this is to define and "empty" CSS PseudoClass. Set the effect on the text as you want it, and then define the effect for text in an empty text field to be null:
.text-field {
-fx-prompt-text-fill: gray;
}
.text-field .text {
-fx-effect: dropshadow( two-pass-box , blue , .5, 10 , 1 , 1);
}
.text-field:empty .text {
-fx-effect: null ;
}
To make the pseudoclass work, you need to register a listener with the text property in the text field and update it:
TextField textField = new TextField();
textField.setPromptText("Enter text");
PseudoClass empty = PseudoClass.getPseudoClass("empty");
textField.pseudoClassStateChanged(empty, true);
textField.textProperty().addListener((obs, oldText, newText) -> {
textField.pseudoClassStateChanged(empty, newText.isEmpty());
});
Here is a SSCCE (with the CSS code above in prompt-text-styling.css):
import javafx.application.Application;
import javafx.css.PseudoClass;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TextFieldPromptStylingTest extends Application {
#Override
public void start(Stage primaryStage) {
TextField textField = new TextField();
textField.setPromptText("Enter text");
PseudoClass empty = PseudoClass.getPseudoClass("empty");
textField.pseudoClassStateChanged(empty, true);
textField.textProperty().addListener((obs, oldText, newText) -> {
textField.pseudoClassStateChanged(empty, newText.isEmpty());
});
Button okButton = new Button("OK");
VBox root = new VBox(10, textField, okButton);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(24));
Scene scene = new Scene(root);
scene.getStylesheets().add("prompt-text-styling.css");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
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.
I want to add another css class for my component by example
.item{
-fx-background-color:blue;
-fx-border-radius:5;
}
.item-some{
-fx-background-color:red;
}
and in my code
control.getStyleClass().addAll("item","item-some");
but my control only get "item-some" style i want override only the color applying the second class as in css on web, can someone help me or give me a link to read about it?
thanks.
This basically behaves as expected for me: the item with both style classes gets the properties defined for both selectors. If there are conflicts, such as fx-background-color in this example, the one defined later in the css file overrides the ones before it.
Here's a complete test:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.stage.Stage;
public class MultipleStyleClassTest extends Application {
#Override
public void start(Stage primaryStage) {
Region region1 = new Region();
Region region2 = new Region();
region1.getStyleClass().add("style-class-1");
region2.getStyleClass().addAll("style-class-1", "style-class-2");
HBox root = new HBox(region1, region2);
Scene scene = new Scene(root);
scene.getStylesheets().add("multiple-style-class-test.css");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
multiple-style-class-test.css is
.style-class-1 {
-fx-min-width: 300 ;
-fx-min-height: 400 ;
-fx-background-color: blue ;
-fx-background-radius: 25 ;
}
.style-class-2 {
-fx-background-color: red ;
}
and the result is
As can be seen, both region1 and region2 get the -fx-min-height, -fx-min-width, and -fx-background-radius properties defined for style-class-1. region1 gets the -fx-background-color defined for style-class-1; region2 displays the background color defined for style-class-2.