3

I use Java8. I'm trying to customize javafx.scene.control.TextField.

Here is my CSS:

-fx-border-width: 1;
-fx-border-radius: 5;
-fx-border-color: #0000ff;
-fx-background-color: #ffffff;

And here is a result:

default TextField

Note it has some "3D-effect", or "inner shadow". Lets look closer:

enlarged TextField

There is some gradient between border and background, while I specified pure blue border and white background in CSS. Playing with different colors and even multilayer background does not remove that "inner shadow" effect. Setting -fx-effect: null; gives nothing.

Funny thing is: when I use less than 4 border lines (or border-radius), that effect "automagically" disappears:

-fx-border-width: 1 1 0 1;

disappeared shadow

And question is: what causes that "inner shadow" effect and how to turn this off? I tried to find out an answer inside modena.css and TextFieldSkin, but found nothing.

PS. I could use a workaround: remove borders at all, and use 2-layered background. But this is ugly and breaks borders-background approach in UI.

UPD

Further investigation gives some results:

  • Any ASYMMETRIC control like -fx-border-width: 1 1 2 1; or -fx-border-radius: 4 4 4 5; is drawn as described in CSS. Looks like JavaFX draws it line-by-line.
  • Any SYMMETRIC control like -fx-border-width: 1 1 1 1; or -fx-border-width: 1; (it is the same) is drawn with "gradient effect". Gradient color and width depend upon -fx-border-radius value. That makes me think of image scaling and pixel blurring.

What happens under-the-hood and is there some "cure" for symmetric controls?

n00bot
  • 229
  • 1
  • 11

1 Answers1

2

Update

On re-reviewing your question, I think your issue is related to a peculiar rendering artifact of rounded borders.

I'll leave the original answer here as I still believe that using layered backgrounds rather than borders is the best solution for most of these kinds of requirements. By using layered backgrounds you are using a proven method that is utilized in many places within the standard Java 8 modena.css. Also with layered backgrounds do not suffer from the particular rendering artifact that you point out in your question.

My guess is that when all four sides of the border are specified, that a different rendering primitive call is made internally to the native graphics library than if you only request for rendering three sides of the border (e.g. there is probably a single native call for four sides and three separate native calls for three sides). This is of course an assumption on my part. Continuing with this assumption I am guessing that the single call for all four sides incurs a slight overflow of the 1 pixel outer border lines into the next inside pixel, resulting in an anti-aliasing of a very light shadow color for the entire outside border. However when you use a request for three sides of the border, the resulting graphics primitive call does not result in aliasing of the outer border.

One comment I would make is the aliasing artifact for the border is not visible to (my) native eye when viewing on a retina MacBook display. To see the artifact, it is necessary for me to snapshot the screen and zoom in 10 fold using a paint program. So for most applications (if it were me), I would not worry about such minor rendering artifacts. You could, if you wish, log a bug report on this to have a developer look into it, in case there is a small calculation rounding error or something like that in the rendering solution for rounded borders.

One way to work-around this is to specify two layered borders instead of one. Have one border layer render three sides of your border and another border layer render the last side, this will not result in a rendering artifact.

For example, with rendering artifact:

TextField aliasedTextField = new TextField();
aliasedTextField.setStyle(
        "-fx-border-width: 1;\n" +
        "-fx-border-radius: 5;\n" +
        "-fx-border-color: #0000ff;\n" +
        "-fx-background-color: #ffffff;"
);

no artifact

Without rendering artifact:

TextField unaliasedTextField = new TextField();
unaliasedTextField.setStyle(
        "-fx-border-width: 0 1 1 1, 1 0 0 0;\n" +
        "-fx-border-radius: 5, 5;\n" +
        "-fx-border-color: #0000ff, #0000ff;\n" +
        "-fx-background-color: #ffffff;"
);

no artifact

Original Answer

The standard .text-input background is defined in Java 8 modena.css as:

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

This defines multiple layers of shaded linear gradients, the first layer being the outside border of the control, the second being the inner background of the control. The second line, specifies that top 5 pixels of the inner background of the control are gradually shaded slightly darker than the rest of the inner background of the control.

I think it is the linear gradients that you are terming as "inner shadows" in your question (note that the implementation is a gradient, not an inner shadow effect).

You can redefine the default style to remove the linear gradients, for example as below:

-fx-background-color: -fx-text-box-border, -fx-control-inner-background;

The first text field below has the standard gradient style, the second text field has the linear gradients removed. The linear gradient effect is quite subtle.

shadows

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 TextFieldShadows extends Application {
    @Override
    public void start(final Stage stage) throws Exception {
        TextField standardTextField = new TextField();
        TextField styledTextField = new TextField();
        styledTextField.setStyle(
                "-fx-background-color: -fx-text-box-border, -fx-control-inner-background;"
        );

        VBox layout = new VBox(10, standardTextField, styledTextField);
        layout.setPadding(new Insets(10));
        stage.setScene(new Scene(layout));
        stage.show();
    }

    public static void main(String[] args) throws Exception {
        launch(args);
    }
}

The rest of this answer might confuse you a little bit, but... Also refer to a related discussion on focus colors:

The -fx-faint-focus-color looked-up color, is defined in the default Java 8 modena.css file..

The default value is this:

-fx-faint-focus-color: #039ED322;

Override it in your custom stylesheet by redefining it for the .root selector:

.root {
    -fx-faint-focus-color: transparent;
}

See also:

If you wish to customize the background gradient styles for your controls, then the best way to do that is via an external CSS stylesheet which defines background style for both the standard unfocused control and the :focused psuedo-style, otherwise you might end up with weird behaviour such as the focus ring not be being displayed as expected for your control (as you might find in the simple sample code I provided above).

Community
  • 1
  • 1
jewelsea
  • 150,031
  • 14
  • 366
  • 406
  • Thanks for reply, but... this is not a solution. You suggest to use 2-layer background, I mentioned this as "workaround". Please append a custom border to your example: "-fx-border-width: 1; -fx-border-radius: 5; -fx-border-color: #0000ff;" and tell me you don't see this gradient... On top of that, focus-color is not a case. Put 10 TextFields inside your layout, and gradient applied to all of them. – n00bot Nov 06 '15 at 07:08
  • I disagree with some of the information in your comment. I am also of the opinion that layered backgrounds are usually superior to border definitions when using JavaFX CSS. But, that is just my opinion, there are other valid opinions and solutions. – jewelsea Nov 06 '15 at 09:07
  • Imagine complicated UI with lots of components inheritance (ImprovedTextField extends CustomTextField extends basic TextField) with lots of custom styles. And you want to highlight some element, say change border and keep BG, or change BG and keep border... It is pain for layered BG. Can't see why is it "superior". But separate border/BG definition helps in such complicated UI. – n00bot Nov 06 '15 at 10:11
  • @n00bot The default stylesheet, modena, predominantly uses layered backgrounds to render borders. By using looked-up colors for the layered border colors, it makes it relatively easy to change the border color (you just redefine a looked-up color). There's a talk somewhere online (can't find it easily) from the JavaFX team where it's stated they chose this route for performance reasons (iirc with hardware graphics acceleration, rendering the solid rectangles one on top of the other is faster than drawing the individual lines around the control). – James_D Nov 06 '15 at 13:36
  • @jewelsea Thanks again, it's a good idea to use layered borders (separately from BG). Agreed, for single Control in simple example, this gradient is hard-to-see and is not a big deal. Why is it such important for me? I created complicated controls, part of them has 4-lines borders, other part has 2- and 3-lines borders. When I put them on a single Pane, naked eye detects that part of UI looks "raised", but other looks "flat". That feels like lack-of-design and absolutely unacceptable. This is why I started "pixel-investigation". – n00bot Nov 10 '15 at 07:34