0

My application contains several different packages that I want to be able to access CSS theme files, stored in the application's themes folder.

After following several tutorials and other StackExchange questions and answers, I haven't found anything that works for my situation. In fact, many of the "answers" just say to move the .CSS files into the same folder as the class calling for it; that is not an acceptable solution for my situation.

My directory structure is similar to this:

- Root Project Folder
    - /data
    - /themes
        - /Theme.css
    - /src
        - sample
            - /Main.java
            - /Controller.java
            - /sample.fxml

How would I apply the Theme.css to my sample.fxml file?

I threw together a sample project that works to illustrate my issue.

Main.java:

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("Hello World");

        Scene scene = new Scene(root, 300, 275);

        scene.getStylesheets().add(
            getClass().getResource("/themes/Theme.css").toExternalForm());

        primaryStage.setScene(scene);
        primaryStage.show();
    }


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

sample.fxml:

<GridPane fx:controller="sample.Controller"
          xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
</GridPane>

The Error:

Caused by: java.lang.NullPointerException
    at sample.Main.start(Main.java:18)

Also Tried:

scene.getStylesheets().add("file:///themes/Theme.css");
scene.getStylesheets().add("file:///../themes/Theme.css");

Those both present the same "resource not found" error.

This code works in the Sample above:

File css = new File("themes/Theme.css");
scene.getStylesheets().add("file:///" + css.getAbsolutePath());

However, I cannot get the exact same code to work within my larger project with the same structure.

I get this error:

WARNING: Resource "file:////[PATH TO PROJECT]/themes/Theme.css" not found

The full path shown in the error is exactly correct and leads to the file that DOES exist.

How would I go about troubleshooting this? Since it works fine in the sample project but not my larger one, the problem obviously lies somewhere in my other project code.

But why would the compiler tell me a file doesn't exist when it clearly does?


Incidentally, my goal here is to allow users to select a "Theme" to use to style the entire application. The /themes folder contains several dozen themes to choose from.

Thank you in advance for all your help!

Zephyr
  • 9,885
  • 4
  • 28
  • 63
  • Check that the CSS file is being deployed to the build folder (details depend on your IDE and project settings), but basically you want to see a `themes` folder containing `Theme.css` in whatever your output folder is. The `themes` folder should be alongside the `sample` folder that contains the *class* files (not the Java source files). – James_D Feb 28 '17 at 19:23
  • I'm using IntelliJ IDEA Ultimate. The build folder does not include the `/themes` directory. I'm curious, though. The other external files I access from my code does not need to be in the build path; what is the reason it is different for CSS? – Zephyr Feb 28 '17 at 19:57
  • I did also manually copy the entire `/themes` folder to the build folder for testing purposes and the same issue occurs. – Zephyr Feb 28 '17 at 19:59
  • I don't use IntelliJ, but the principal is the same. As for the other files you mention, of course they need to be deployed along with the application, otherwise how would they be accessed at runtime. How are you running the application? Is it running from the file system, or is IntelliJ building a jar file and running it from the jar file? – James_D Feb 28 '17 at 20:00
  • I have not even begun the process of deploying. This is just within the testing environment. When running, IntelliJ compiles and runs from the filesystem. When deploying, I would obviously include the extra files. – Zephyr Feb 28 '17 at 20:02
  • I mean "deploying" in the loosest sense. IntelliJ is compiling the source files and putting the class files somewhere, and then its running those with the classpath set to the root folder where the class files are created ("deployed", in the sense I am using it). If you use `getClass().getResource(...)` with a leading `/`, it will be interpreted relative to the classpath, so the CSS file needs to be copied ("deployed") to the same location. – James_D Feb 28 '17 at 20:05
  • (IMHO there's no point in trying to get this working with `file://`, as it definitely won't work when you properly deploy the application. But if you do want to do that, use `File css = new File("themes/Theme.css");` and then `scene.getStylesheets().add(css.toURI().toString());`. Don't try to create the URL yourself - it won't work if your file path has characters that are not legal in a URL - whitespace for example.) This assumes the working directory is the same as the classpath, which it probably is (but may not be). – James_D Feb 28 '17 at 20:08
  • I will give that a try, James. Thank you. How would you recommend I implement this, though. Because I agree it could cause problems when deploying as is. – Zephyr Feb 28 '17 at 20:17
  • The recommended approach is to bundle the CSS file in the jar file (so you just make the folder containing it part of the build path), then use `getClass().getResource(...)`. I think of `getClass().getResource(...)` as meaning "load a resource from the same place you loaded this class": i.e. if you're running from the file system, it loads the resource from the file system. If you're running from a jar file, it loads the resource from the jar file, etc. The bottom line is that if `getClass().getResource()` isn't working, the CSS file is not in the expected location relative to the classpath. – James_D Feb 28 '17 at 20:20
  • See related: [How to convert a normal java project in intellij into a JavaFx project](http://stackoverflow.com/questions/23421325/how-to-convert-a-normal-java-project-in-intellij-into-a-javafx-project) – jewelsea Feb 28 '17 at 22:43

1 Answers1

0

Following @James_D's comment to the original question, I was able to get this working with the following code, converting the string to a URI to get the proper format.

I did not want to bundle the CSS files within the JAR because I want my users to be able to customize them and add their own.

Solution:

        scene.getStylesheets().clear();

        File css = new File("themes/" + Settings.getThemeName() + ".css");
        System.out.println(css.toString());
        File fontFile = new File("themes/Font.css");

        scene.getStylesheets().addAll(
                css.toURI().toString(),
                fontFile.toURI().toString());
Zephyr
  • 9,885
  • 4
  • 28
  • 63