Search code examples
buttonjavafxdropshadow

Why does dropshadow on a button in JavaFX not work the same as on a pane


See this part of a screen cap.

button dropshadow

The first dropshadow I added to this FXML page was for the white panes, I used

-fx-effect: dropshadow(three-pass-box, rgba(16, 40, 123, 0.2), 0px, 0px, 4px, 4px);

in css and it works like we'd want it to

Then I copied the same dropshadow to the buttons (even though we might want to adjust the thickness or color one way or the other) and somehow the dropshadow now also shadows the button itself. The color of the upper button should be white, just as the text on the lower button, but those css-settings now seem to be overruled by the dropshadow which should only be performed outside the buttons, not within its borders

Is this some kind of bug or some configuration or setting that can fix this?

Java 21 (openjdk-21.0.0)

Code for MRE, starting with pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.3</version>
    <relativePath/>
  </parent>

  <groupId>nl.mre</groupId>
  <artifactId>MRE</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>
  <properties>
    <maven.compiler.release>21</maven.compiler.release>
    <spring.boot.version>3.2.3</spring.boot.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.openjfx</groupId>
      <artifactId>javafx-controls</artifactId>
      <version>21</version>
    </dependency>
    <dependency>
      <groupId>org.openjfx</groupId>
      <artifactId>javafx-fxml</artifactId>
      <version>21</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
      <version>${spring.boot.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>${spring.boot.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>${spring.boot.version}</version>
      <type>pom</type>
    </dependency>
  </dependencies>
</project>

SpringBootApp.java

package nl.mre;

import javafx.application.Application;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootApp {
    public static void main(String[] args) {
        Application.launch(JavaFXApplication.class, args);
    }
}

JavaFXApplication.java

package nl.mre;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan
public class JavaFXApplication extends Application {
    private Parent rootNode;

    private Stage mainStage;

    @Override
    public void init() {
        ConfigurableApplicationContext mainContext = SpringApplication.run(JavaFXApplication.class);
        Platform.runLater(() -> {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/Mre.fxml"));
            loader.setControllerFactory(mainContext::getBean);
            try {
                rootNode = loader.load();
            } catch (Exception e) {
                //no logging in MRE
            }
        });
    }

    @Override
    public void start(Stage primaryStage) {
        mainStage = primaryStage;
        mainStage.setScene(new Scene(rootNode));
        mainStage.setResizable(false);
        mainStage.show();
    }

    @Override
    public void stop() {
        mainStage.close();
        System.exit(0);
    }
}

/resources/fxml/Mre.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.Region?>

<BorderPane prefHeight="648.0" prefWidth="1152.0" stylesheets="@../css/application.css" xmlns="http://javafx.com/javafx/21">
    <center>
        <Pane prefHeight="600" prefWidth="1152.0" styleClass="pane-background">
            <Label alignment="CENTER" layoutX="20.0" layoutY="122.0" prefHeight="35.0" prefWidth="390.0" styleClass="label-big-and-bold" text="Nieuw" />
            <Pane layoutX="20.0" layoutY="176.0" prefHeight="264.0" prefWidth="390.0" styleClass="pane-white">
                <TextField alignment="BOTTOM_LEFT" layoutX="20.0" layoutY="20.0" prefHeight="40.0" prefWidth="350.0" styleClass="textfield" />
                <Label layoutX="24.0" layoutY="20.0" prefHeight="15.0" prefWidth="110.0" styleClass="label-small" text="A *" textAlignment="RIGHT" />
                <TextField alignment="BOTTOM_LEFT" layoutX="20.0" layoutY="75.0" prefHeight="40.0" prefWidth="350.0" styleClass="textfield" />
                <Label layoutX="24.0" layoutY="75.0" prefHeight="15.0" prefWidth="140.0" styleClass="label-small" text="B *" />
                <Button disable="true" layoutX="105.0" layoutY="135.0" mnemonicParsing="false" prefHeight="44.0" prefWidth="180.0" styleClass="button-audio" text="ALLEEN AUDIO" />
                <Region layoutX="115.0" layoutY="145.0" opacity="0.4" prefHeight="24.0" prefWidth="24.0" styleClass="region-audio" />
                <Button disable="true" layoutX="105.0" layoutY="195.0" mnemonicParsing="false" prefHeight="44.0" prefWidth="180.0" styleClass="button-video" text="AUDIO EN VIDEO" />
                <Region layoutX="115.0" layoutY="209.0" prefHeight="24.0" prefWidth="24.0" styleClass="region-video" />
            </Pane>
        </Pane>
    </center>
</BorderPane>

/resources/css/application.css

.button-primary, .button-agree, .button-login, .button-start, .button-stop, .button-video {
    -fx-effect: dropshadow(three-pass-box, rgba(16, 40, 123, 0.2), 0px, 0px, 4px, 4px);
    -fx-background-color: #10277b;
    -fx-border-radius: 5;
    -fx-font-size: 14;
    -fx-font-weight: bold;
    -fx-font-family: "Open Sans";
    -fx-text-fill: #ffffff;
}

.button-agree, .button-login, .button-start, .button-stop, .button-video {
    -fx-background-position: left center;
    -fx-background-repeat: no-repeat;
    -fx-padding: 10 12 10 40;
}

.region-video {
    -fx-background-color: white;
    -fx-min-height: 16;
    -fx-min-width: 24;
    -fx-max-height: 16;
    -fx-max-width: 24;
    -fx-shape: "M17.016 10.5l3.984-3.984v10.969l-3.984-3.984v3.516c0 0.563-0.469 0.984-1.031 0.984h-12c-0.563 0-0.984-0.422-0.984-0.984v-10.031c0-0.563 0.422-0.984 0.984-0.984h12c0.563 0 1.031 0.422 1.031 0.984v3.516z";
}


.button-secondary, .button-audio, .button-back, .button-cancel, .button-remark {
    -fx-background-color: white;
    -fx-border-color: #10277b;
    -fx-border-radius: 5;
    -fx-border-width: 2;
    -fx-font-size: 14;
    -fx-font-weight: bold;
    -fx-font-family: "Open Sans";
    -fx-text-fill: #10277b;
    -fx-effect: dropshadow(three-pass-box, rgba(16, 40, 123, 0.2), 0px, 0px, 4px, 4px);
}

.button-audio, .button-back, .button-cancel, .button-remark {
    -fx-background-position: left center;
    -fx-background-repeat: no-repeat;
    -fx-padding: 10 12 10 40;
}

.region-audio {
    -fx-background-color: #10277b;
    -fx-min-height: 24;
    -fx-min-width: 16;
    -fx-max-height: 24;
    -fx-max-width: 16;
    -fx-shape: "M17.297 11.016h1.688c0 3.422-2.719 6.234-6 6.703v3.281h-1.969v-3.281c-3.281-0.469-6-3.281-6-6.703h1.688c0 3 2.531 5.063 5.297 5.063s5.297-2.063 5.297-5.063zM12 14.016c-1.641 0-3-1.359-3-3v-6c0-1.641 1.359-3 3-3s3 1.359 3 3v6c0 1.641-1.359 3-3 3z";
}

.label {
    -fx-text-fill: #10277b;
    -fx-font-size: 16;
    -fx-font-family: "Open Sans";
}

.label-big-and-bold {
    -fx-text-fill: #10277b;
    -fx-font-size: 24;
    -fx-font-weight: bold;
    -fx-font-family: "Open Sans";
}

.label-small {
    -fx-text-fill: #10277b;
    -fx-font-size: 10;
    -fx-font-family: "Open Sans";
}

.pane-background {
    -fx-background-color: #f2f4f8;
}

.pane-white {
    -fx-border-radius: 10;
    -fx-background-color: white;
    -fx-background-radius: 10;
    -fx-effect: dropshadow(three-pass-box, rgba(16, 40, 123, 0.2), 0px, 0px, 4px, 4px);
}


.text-field {
    -fx-background-color: #f2f4f8;
    -fx-border-color: #3596d2;
    -fx-border-width: 0 0 2 0;
    -fx-text-fill: #10277b;
    -fx-font-size: 14;
    -fx-font-family: "Open Sans";
}

.text-field:focused {
    -fx-background-color: -fx-focus-color, #FFFFFF;
    -fx-background-insets: -0.4, 2, -2.8, 5.2;
}

Solution

  • The problem arises because your buttons are disabled, and the default style defined in modena.css sets the opacity for disabled items to 0.4. Thus your buttons are partially transparent and you can see the shadow through them.

    If you turn off the opacity setting for disabled buttons, e.g. with

    .button:disabled {
        -fx-opacity: 1.0;
    }
    

    it will fix the problem. You probably want some kind of visual clue that the button is disabled, so this is unlikely to be the exact fix you need, but it demonstrates what is causing the issue.