JavaFX apply rotate to an image from CSS - javafx

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

Related

JavaFX Text-Area remove borders

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.

Javafx linear-gradient repeat behaviour

Javafx linear-gradient repeat seems to reflect the colours rather than repeat.
I wrote a simple application to show what I see when using linear-gradient with repeat to create a striped pattern in my application on a custom Node (a StackPane). In my application this are added as overlays to a XYChart and their height varies. Using a Rectangle wasn't working well which is why I use a Stackpane and set a style on it rather than creating the LinearGradient programmatically.
The colour list is dynamic and varies in size in the application.
The issue is the way linear-gradient flips the list and reflects the colours on each repeat rather than just repeat.
This link describes a similar issue but just adding in endless stops seemless like a messy solution for my issue, it would be much better to add the colours once and repeat.
linear gradient repeat on css for javafx
java.util.List;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
List<Color> colors = Arrays.asList( Color.RED,Color.BLUE,Color.YELLOW,Color.GREEN);
StackPane stackPane = new StackPane();
stackPane.setStyle(getLinearGradientStyle(colors));
root.setCenter(stackPane);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
catch (Exception e) {
e.printStackTrace();
}
}
private String getLinearGradientStyle(List<Color> colors) {
StringBuilder stringBuilder = new StringBuilder("-fx-background-color: linear-gradient(from 0px 0px to 10px 10px, repeat,");
for (int i = 0; i < colors.size(); i++) {
stringBuilder.append("rgb(")
.append((int) (colors.get(i).getRed() * 255)).append(",")
.append((int) (colors.get(i).getGreen() * 255)).append(",")
.append((int) (colors.get(i).getBlue() * 255))
.append(")")
.append(" ").append(getPercentage(i+1, colors.size()+1) );
if (i < colors.size() - 1) {
stringBuilder.append(",");
}
}
stringBuilder.append(");");
System.out.println("Main.getLinearGradientStyle():"+stringBuilder);
return stringBuilder.toString();
}
private String getPercentage(float i, int size) {
return (((1.0f / size) * 100 )*i)+ "%";
}
public static void main(String[] args) {
launch(args);
}
Here's a CSS3 example using repeating-linear-gradient:
https://tympanus.net/codrops/css_reference/repeating-linear-gradient/
scroll down to the following text: will create a striped background, where each linear gradient is a three-stripe gradient, repeated infinitely (this is the example)
My example uses a diagonal pattern which is what I need but the above example shows what I'd like to see in terms of solid repeating colours with out reflection in normal css.
Thanks for any help
This looks like a bug. If you run the following example (moved the CSS into a file):
Main.java
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Region;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
Region region = new Region();
region.backgroundProperty().addListener((obs, ov, nv) ->
System.out.println(nv.getFills().get(0).getFill()));
Scene scene = new Scene(region, 500, 300);
scene.getStylesheets().add("Main.css");
primaryStage.setScene(scene);
primaryStage.show();
}
}
Main.css
.root {
-fx-background-color: linear-gradient(from 0px 0px to 10px 10px, repeat, red 20%, blue 40%, yellow 60%, green 80%);
}
You'll see the following printed out:
linear-gradient(from 0.0px 0.0px to 10.0px 10.0px, reflect, 0xff0000ff 0.0%, 0xff0000ff 20.0%, 0x0000ffff 40.0%, 0xffff00ff 60.0%, 0x008000ff 80.0%, 0x008000ff 100.0%)
As you can see, despite using "repeat" in the CSS the LinearGradient that is created uses "reflect".
There is likely nothing you can do about this bug yourself, but if you don't mind setting the background in code (or probably even FXML) then the following should do what you want:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
LinearGradient gradient = new LinearGradient(0, 0, 10, 10, false, CycleMethod.REPEAT,
new Stop(0.2, Color.RED),
new Stop(0.4, Color.BLUE),
new Stop(0.6, Color.YELLOW),
new Stop(0.8, Color.GREEN)
);
Region region = new Region();
region.setBackground(new Background(new BackgroundFill(gradient, null, null)));
Scene scene = new Scene(region, 500, 300);
primaryStage.setScene(scene);
primaryStage.show();
}
}
You can move the creation of the LinearGradient into a method that takes an arbitrary number of Colors, just like you're currently doing.
If you're interested, I believe the bug is located in javafx.css.CssParser around line 1872 (in JavaFX 12):
CycleMethod cycleMethod = CycleMethod.NO_CYCLE;
if ("reflect".equalsIgnoreCase(arg.token.getText())) {
cycleMethod = CycleMethod.REFLECT;
prev = arg;
arg = arg.nextArg;
} else if ("repeat".equalsIgnoreCase(arg.token.getText())) {
cycleMethod = CycleMethod.REFLECT;
prev = arg;
arg = arg.nextArg;
}
As you can see, it erroneously sets the CycleMethod to REFLECT when the text is equal to "repeat".
A bug report has been filed: JDK-8222222 (GitHub #437). Fix version: openjfx13.

How to hide the TabBar in TabPane?

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;
}

how about css class prescedence in javafx

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.

JAVAFX 2.0 How can i change the slider icon in a slider to an image?

I want to change the icon to my image, I've looked through the CSS reference guide but I can't seem to find anything relevant. Is it even possible? Doesn't matter if it is using CSS or declaratively from main JavaFX script.
Take a look at the sample code and images of how a custom slider is rendered in this AudioPlayer.
Also the JFXtras library has numerous gauges if you just want feedback rather than an interactive control.
Here is some sample css using the selector pointed out by invariant's answer. Note that I needed to add an -fx-padding specification at half the images dimensions in order for the whole image to display.
/** slider.css
place in same directory as SliderCss.java
ensure build system copies the css file to the build output path */
.slider .thumb {
-fx-background-image :url("http://icons.iconarchive.com/icons/double-j-design/diagram-free/128/piggy-bank-icon.png");
-fx-padding: 64;
}
/* Icon license: creative commons with attribution: http://www.doublejdesign.co.uk/products-page/icons/diagram */
Sample app:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class SliderCss extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) throws Exception {
VBox layout = new VBox();
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10px;");
layout.getChildren().setAll(new Slider());
layout.getStylesheets().add(getClass().getResource("slider.css").toExternalForm());
stage.setScene(new Scene(layout));
stage.show();
}
}
Sample program output:
you can change thumb of slider using css
.slider .thumb{
-fx-background-image :url("your image");
...// more customization
}
I know that this is an old question but I think I have a contribution to this solution.
If we want to use a unique slider or we want to modify the appearance of all the sliders the previous solution is more than enough. However, if we need only modify the appearance of only one slider the we need another approach.
What we gonna do is imagine that we have applied a based style to the main scene. But we don't want to add another css file just to modify the behavior of the slider. So the question is: How we can modify the slider style using our base css file?
The solution is simple setId() all the controls have this attribute. Now let's check out this class:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Slider;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
* Created by teocci.
*
* #author teocci#yandex.com on 2018-Jul-06
*/
public class CustomSliderThumb extends Application
{
public static void main(String[] args) { launch(args); }
#Override
public void start(Stage stage) throws Exception
{
Slider slider = new Slider();
slider.setId("custom-slider");
VBox layout = new VBox();
layout.setId("base-layout");
layout.getChildren().setAll(slider);
Scene scene = new Scene(layout);
scene.getStylesheets().add("css/style.css");
stage.setScene(scene);
stage.show();
}
}
In this example, we created a slider and set its id as "custom-slider". Then we added this slider to a VBox layout and, finally, we added the layout to the scene that has the style.css
Now lets check the style.css and how to use the id selector to apply a custom style. Remember to specify -fx-pref-height and -fx-pref-width with the dimensions of the image or if is a square -fx-padding at half the image side dimension for displaying the whole image.
#custom-slider .thumb {
-fx-background-image :url("https://i.imgur.com/SwDjIg7.png");
-fx-background-color: transparent;
-fx-padding: 24;
/*-fx-pref-height: 48;*/
/*-fx-pref-width: 48;*/
}
#custom-slider .track {
-fx-background-color: #2F2F2F;
}
#base-layout {
-fx-background-color: lightgray;
-fx-padding: 10px;
}
Sample program output:
If you want to remove the thumb background color and only have the image(semi-transparent ones like round button) then you should also -fx-background-color:transparent; unless you will have the background
.slider .thumb {
-fx-background-image :url("sider-round-thumb-image.png");
-fx-padding: 16; /* My thumb image is 33x33 pixels,so padding is half */
-fx-pref-height: 28;
-fx-pref-width: 28;
-fx-background-color:transparent;
}

Resources