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.
Related
I have multiple buttons in in my javafx appliaction with mnemonics.
When I press "Alt" the mnemonics appear in a dark color but I want them to be white.
What is the right css selector for this?
I tried:
.mnemonic-underline: {
-fx-stroke: white;
}
But after that the underlines are visible all the time.
This should work:
:show-mnemonics > .mnemonic-underline {
-fx-stroke: white;
}
Example program:
public class MnemonicStylingSSCCE extends Application {
#Override
public void start(Stage stage) {
// Init label
final Label mnemonic = new Label("_Mnemonic");
mnemonic.setMnemonicParsing(true);
// Init scene
final Scene scene = new Scene(mnemonic);
scene.getStylesheets().add(MnemonicStylingSSCCE.class.getResource("mnemonic.css").toExternalForm());
stage.setScene(scene);
// Request focus & show
stage.requestFocus();
stage.show();
}
}
Side note - the content of mnemonic.css is CSS shown above (but with the red color instead of white).
Thank you ahead of time for your time taken.
Currently, I am in the process of creating a JavaFX GUI for a simple-enough client/server application.
On the right side of a SplitPane is a GridPane, where-by every time a message is sent or received, that Message is displayed within the new ROW in the GridPane, and the message is basically an ImageView(image) followed by a TextArea with a String in it displaying the message sent/received.
My issue is that I cannot figure out after over a week how to size the TextArea appropriately for the block of text within it.
Before you mark this question as a duplicate, I have tried every implementation I could find.
Firstly, the ScrollBar listening solution does not work on runtime, this only appears to work WHILE a user is typing, so I have scratched that as a potential solution for my particular issue.
The solution I'm currently using (which isn't working) is using a Text object and getting the layout bounds/height of THAT for the TextArea.
I am fine with my TextAreas (acting as message bubbles) all being the same width, as of now I am specifying the minWidth to be 300.0, the problem again is with the HEIGHT.
My code is as follows:
HBox messageBox = new HBox(10);
messageBox.setPadding(new Insets(0, 0, 0, 25));
TextArea textArea = new TextArea();
textArea.setText(message);
textArea.setFont(new Font(20));
textArea.setWrapText(true);
final Text helper = new Text();
helper.setText(message);
helper.setFont(textArea.getFont());
helper.setWrappingWidth(300.0);
double width = helper.getLayoutBounds().getWidth();
double height = helper.getLayoutBounds().getHeight();
textArea.setMinWidth(width);
textArea.setPrefHeight(height);
messageBox.getChildren().addAll(imageView, textArea);
messagePane.add(messageBox, 0, rowCount);
rowCount++;
Please note that I have also tried placing my helper Text object into a throw-away Pane, which renders almost identical results.
Lastly, I have tried adding padding to the setPrefHeight() of the TextArea, I have tried MinHeight/MaxHeight combinations.
This picture illustrates my FIRST problem, the 3rd message has far too much space below the end of the block of text, while preceding message look fine, (IMO). The second picture BELOW demonstrated my 2nd problem, larger blocks of text seem to gradually decrease the width of the TextAreas or perhaps the HBox's above them. Before these subsequent HBox's were, added, the highlighted TextArea had enough space, for instance.
Is there any solution that will work for my needs?
I would be very grateful, thank you for your time!
Keith
This is not a trivial task (unless you find a workaround), I am afraid you will have to somehow to compute the actual width and height and apply it to the TextArea. The way I am thinking is to either find your magic numbers by trial and error approach or better take the message text add it to a Label and then compute its dimensions (width, height) and then use those in order to set the TextArea. Here is a small example :
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class MessagerTest extends Application {
private VBox displayPane = new VBox(5);
private TextArea messageArea = new TextArea();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
BorderPane mainPane = new BorderPane();
ScrollPane scrollPane = new ScrollPane(displayPane);
displayPane.setPadding(new Insets(10));
displayPane.prefWidthProperty().bind(scrollPane.widthProperty());
scrollPane.prefWidthProperty().bind(mainPane.widthProperty());
scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
mainPane.setCenter(scrollPane);
mainPane.setBottom(messageArea);
mainPane.setPadding(new Insets(10));
messageArea.setPrefHeight(120);
messageArea.setFont(Font.font(16));
messageArea.setWrapText(true);
messageArea.setPromptText("Type a message here...");
messageArea.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ENTER && !e.isShiftDown()) {
sendMessage(messageArea.getText());
e.consume();
} else if (e.getCode() == KeyCode.ENTER && e.isShiftDown()) {
messageArea.appendText(System.lineSeparator());
}
});
mainPane.getStylesheets().add(this.getClass().getResource("messanger.css").toExternalForm());
stage.setScene(new Scene(mainPane, 450, 600));
stage.show();
}
private void sendMessage(String message) {
TextArea txtArea = new TextArea(message);
txtArea.setWrapText(true);
txtArea.setId("Message");
txtArea.setEditable(true);
resizeTextArea(txtArea);
displayPane.getChildren().add(txtArea);
messageArea.clear();
}
private void resizeTextArea(TextArea txtArea) {
String text = txtArea.getText();
double maxWidth = displayPane.getWidth() - 40;
HBox h = new HBox();
Label l = new Label(text);
l.setFont(Font.font(15));
h.getChildren().add(l);
Scene s = new Scene(h);
l.impl_processCSS(true);
l.applyCss();
double width = l.prefWidth(-1) + 20;
double height = l.prefHeight(-1) + 20;
if (width > maxWidth) {
txtArea.setMaxWidth(maxWidth);
txtArea.setMinWidth(maxWidth);
} else {
txtArea.setMaxWidth(width);
txtArea.setMinWidth(width);
}
txtArea.setMinHeight(height);
txtArea.setMaxHeight(height);
}
}
In case you want the CSS file too :
#Message {
-fx-background-color : transparent;
-fx-font-size : 15px;
-fx-text-fill: black;
-fx-display-caret:false;
}
#Message .content:pressed {
-fx-background-color: #E5E4E4;
}
#Message .content {
-fx-background-color: #F1F0F0;
}
.scroll-pane > .viewport {
-fx-background-color: white;
}
The problem with the above is that when you write everything is one line and let the TextArea wrap the text this cause the actual label height to be bigger so you will have to adjust the values a bit in that case.
To be honest I am not sure if this is the only approach you can take or if its the optimal solution. I believe its worth to lose the mouse selection of the text and use a Label instead of doing the above with the TextArea.
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 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;
}
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.