#Should I use FXML to make a UI?

1 messages Β· Page 1 of 1 (latest)

deep cave
#

Hi im not rlly that good with making UI's in JFX. I want to make a simple UI for a fluid simulation and I am not sure on what I should be using or where to start. Here are some mock ups I have made for the simulation.

Does anyone have any ideas on what I can use? I will be using the same dark theme throughout my entire program so I think having only a few CSS files for everything would be good.

neat ospreyBOT
#

<@&987246487241105418> please have a look, thanks.

#

Here is an AI assisted attempt to answer your question πŸ€–. Maybe it helps! In any case, a human is on the way πŸ‘. To continue talking to the AI, you can use </chatgpt:1108714622413963314>.

#

are a few things to consider when deciding whether to use FXML for your UI:

  1. Complexity of the UI: If your UI is simple and doesn't require much customization or dynamic behavior, you may not need to use FXML. You can directly create and configure JavaFX nodes programmatically.

  2. Separation of concerns: FXML allows you to separate the UI design from the logic by defining the structure and appearance of the UI in an XML-like format. This can make your code more maintainable and easier to understand.

  3. Visual design tools: If you prefer using visual design tools, FXML can be a good choice as it is compatible with tools like Scene Builder, which allow you to drag and drop components onto a canvas.

  4. Collaboration: If you are working in a team where designers and developers collaborate on the UI, FXML can facilitate this collaboration as designers can work on the FXML files while developers focus on the logic.

  5. Learning curve: If you are not familiar with FXML or XML-based languages, there may be a learning curve involved in understanding how to define your UI using FXML.

In summary, if your UI is simple and doesn't require much customization or dynamic behavior, you may not need to use FXML. However, if you prefer separating concerns, using visual design tools, collaborating with designers, or if your UI is more complex, then using FXML could be beneficial.

hoary pine
#

yeah you should always use FXML for ui in javafx

deep cave
#

hmm

#

ok

#

also

#

how does it really work?

#

what makes it easier than just using simple jfx

hoary pine
#

I want to make a simple UI for a fluid simulation and I am not sure on what I should be using or where to start.
I would simply use Canvas

hoary pine
#

also SceneBuilder is drag/drop software

#

to easily create ui

deep cave
#

Ahh ok

hoary pine
#

also you might be interested reading this:
#today-i-teach message

#

its about animation package in javafx

deep cave
#

oh

#

i was thinking of using a delta time game loop

#

lmao

hoary pine
#

you can

#

but this is recommended

deep cave
#

i made a fluid simulation in awt so im just bringing all of that to jfx

hoary pine
#

though delta time might be more accurate

deep cave
deep cave
#

hey @hoary pine know what i did wrong here?

I created a Scene class which i extend from and i want to use controller but i can't use it for some reason?

hoary pine
#

make it protected

#

private wont give you access in subclasses

#

also not sure if you want to structure it like this

lapis spindle
#

i woulent recommend making a type named Scene either, can confuse those reading your code

#

especially if you're posting the code to get help

deep cave
#

what should i call it instead?

#

i didn't knwo what to call it 😭

lapis spindle
#

well it represents a state of your program (menu, simulation, etc..), so you could name it something related to that

deep cave
#

hmm

lapis spindle
#

or even just AppScene would be better

deep cave
#

yeahh

#

or ProgramState?

lapis spindle
#

sure

deep cave
#

it sounds cooler

#

yay

hoary pine
#

what is the use of this abstract superclass in the first place?

deep cave
#

tbh

#

i saw a bunch of scenes

#

and each scene will be implementing the same methods

#

and have similar layouts

#

so i thought grouping them would make it easier to work with

#

my cs teacher said it's a good idea to do taht as well

hoary pine
#

yeah grouping them is a good idea but I wouldnt do it like this

deep cave
#

oh

#

how should i do it?

hoary pine
#

probably some enum that stores all your fxml scenes

#

which you then manage

#

you are going to have one class which extends Application

deep cave
#

umm

deep cave
hoary pine
#

javafx.application.Application is your starting point in an javafx application

deep cave
#

yep

hoary pine
#

the start method provides an Stage which is your main window

#

in order to switch javafx.stage.Scene (derived from fxml file) you are going to call Stage#setScene

deep cave
#

yep

#

so you're essentially saying to use something like this?

hoary pine
#

yeah for each fxml create an enum entry

deep cave
#

and then i can refer to each scene by using the corresponding constant?

hoary pine
#

which will maybe hold the file name as field

deep cave
#

ahh

#

i don't wanna annoy you anymore but is it fine if i could ask one more question?

hoary pine
#

yeah all good

#

just ask

deep cave
#

ok thanks

#

so with the enums right

#

i haven't really used them but aren't they just values that you know aren't going to change?

#

so how could i use them to group together common functionality?

hoary pine
#

though I would create another class which manages the Scenes

#

like a Manager class

deep cave
#

ahh

#

and then

#

each scene would have it's own specialised manager

hoary pine
#

nah

#

let me show you a prototype:

deep cave
#

yeah sure that would be great

#

my mums calling me so i'll brb but do carry on i will try to come back asap

hoary pine
#
// would name it Scene/View or smth but all these are already used by javafx
enum Presentation {
    MAIN("main.fxml"),
    SETTINGS("settings.fxml");
    // add more scenes if needed
    
    // path of .fxml file to load later
    private final String path;

    Presentation(String path) {
        this.path = path;
    }
    
    // maybe add additional methods
}
public class Manager {
    
    private final Stage window;
    private final Map<Presentation, Scene> scenes = new HashMap<>();

    public Manager(Stage window) {
        this.window = window;

        // load all Presentations and their Scene with FXMLLoader
        // maybe even remove the Map and put the Scene into the enum as a field and load it there
        // though loading in enum constructor might be a bad idea
        // maybe even load if someone calls Presentation#getScene and if its not loaded yet
    }    
    
    public void switch(Presentation presentation) {
        // do some stuff
        window.setScene(scenes.get(presentation));
    }
}
#

you can extend that with creating an interface for all Controller:

public interface Controller<T> {
    void init(T t);
    void reset();
}
#

or smth like that

deep cave
#

Ok im back

#

time to have a read

deep cave
#

is it for the specific enum?

hoary pine
#

for the args ig

#

if you want to provide an init method

#

but only if needed

#

but I would only add the functionality of storing the controller if needed

deep cave
deep cave
deep cave
#

or in another map

#

also i think i might make controller an abstract class rather than an interface

hoary pine
#

maybe change the enum to this:

enum Presentation {
    MAIN("main.fxml"),
    SETTINGS("settings.fxml");

    private final String path;
    private Scene scene = null;
    private Controller<?> controller = null;
    
    Presentation(String path) {
        this.path = path;
    }
    
    public Scene getScene() {
        if (scene == null) {
            // load and store in scene if called the first time
            // would load the Controller<?> as well through this
        }
        return scene;
    }    
    
    
}
deep cave
#

how does this look?

#

wait i'll copy the ccode instead

#
package com.fluidity.structure;

import javafx.stage.Stage;

public class Manager {
    private final Stage window;

    public Manager(final Stage window){
        this.window = window;

        ProgramState programStateToAdd = ProgramState.MAIN_MENU;
        switchScene(programStateToAdd);
    }

    public void switchScene(ProgramState programState) {
        window.setTitle(programState.name());
        window.setResizable(false);
        window.setScene(programState.getScene());
    }
}
package com.fluidity.structure;

import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;

import java.io.IOException;

// Would be preferable to name it Scene/View, but it would override JFX classes
public enum ProgramState {
    MAIN_MENU("MainMenu.fxml"),
    SETTINGS("Settings.fxml");

    // path of .fxml file to load later
    private final String path;
    private Scene scene = null;
    private Controller controller = null;

    ProgramState(String path) {
        this.path = path;
    }

    public Scene getScene() {
        try {
            if (scene == null){
                FXMLLoader loader = new FXMLLoader(getClass().getResource(this.path));
                this.controller = loader.getController();
                this.scene = new Scene(loader.load(), 1280, 720);
            }
            return scene;
        } catch (IOException e) {
            System.out.println("Scene Cannot be Loaded: " + e.getMessage() );
            return null;
        }
    }

}
hoary pine
#

looks good

#

but I would maybe throw some error if IOException happens

#

instead of returning null

deep cave
hoary pine
#
MAIN_MENU("MainMenu.fxml"),

these paths should start with /
so

MAIN_MENU("/MainMenu.fxml"),
#

also they should be located in the resources folder

deep cave
#

ohhh

#

still bugging

#

hmm

#

oh ok

#

still throwing errors 😭

hoary pine
#

cast class exception

#

show your controller

deep cave
#

?

#

now it opens up the application but no window pops up 😭

hoary pine
#

show your class which extends Application

#

your entry point

#

you might forgot to call stage.show()

deep cave
#

πŸ˜“

#

i forgot that

#

LMAO

#

WHITE SCREEN

#

YAY

hoary pine
#

gg

deep cave
#

TYSMMM

hoary pine
#

now you can easily switch scenes

deep cave
#

YES

neat ospreyBOT
#

@deep cave

Your question has been closed due to inactivity.

If it was not resolved yet, feel free to just post a message below
to reopen it, or create a new thread.

Note that usually the reason for nobody calling back is that your
question may have been not well asked and hence no one felt confident
enough answering.

When you reopen the thread, try to use your time to improve the quality
of the question by elaborating, providing details, context, all relevant code
snippets, any errors you are getting, concrete examples and perhaps also some
screenshots. Share your attempt, explain the expected results and compare
them to the current results.

Also try to make the information easily accessible by sharing code
or assignment descriptions directly on Discord, not behind a link or
PDF-file; provide some guidance for long code snippets and ensure
the code is well formatted and has syntax highlighting. Kindly read through
https://stackoverflow.com/help/how-to-ask for more.

With enough info, someone knows the answer for sure πŸ‘

hoary pine
#

thats your custom made functionality

#

you let all controller implement from Controller<T> and then later when switching scenes in your manager you also call reset on the old controller if it might need to clear smth while not being used and init on the new scene controller to initialise stuff that might be needed for that controller

#

you need to implement that in your manager though

#

and is optional

deep cave
#

when would i need to clear smth though?

hoary pine
#

imagine one scene uses some http connection which is not used by other scenes

#

you might want to close it

#

when the scene is not open

#

smth like that

deep cave
#

ahh fair enough

#

yeah i don't think i would need it then because i am not doing any networking in a fluid simulation lol

hoary pine
#

yeah ofc

#

but that can include anything that needs to be closed like files etc

#

not only networking

#

there are many use cases of init/reset methods

deep cave
#

but idk if it's just me but don't you just close files straight after you use them?

#

don't you do that stuff like filereader.close()

#

i forgot what it was

hoary pine
#

yeah most of the time

hoary pine
#

but its the same thing

#

just dont implement that functionality if you dont need it

deep cave
#

ok that's fine then

#

tysm!

deep cave
#

but im not sure if its stupid or smart

#

basically I was thinkign to have controller as an abstract class

#

and then extend all my other controllers off it

#

because every single scene other than the homepage scene has a previous scene

#

which i believe i can set in common

hoary pine
#

what is the use of knowing about the previous scene?

deep cave
#

i can go back a page

hoary pine
#

yeah you could do that with an abstract class

#

you could also combine that with interface + abstract class

#

anything works here kinda

deep cave
#

also with the controllers, say if i havve a button which i want to shift to a different scene, how do i make the controller access the manager class?

hoary pine
#
interface Controller {
    // common functionality to ALL controller including home page
}

// fix naming
abstract ControllerWithPrevious implements Controller {
    // store previous scene ...
}
#

smth like that

#

and home page would implement from Controller and other which have an previous scnee would extend from ControllerWithPrevious

#

thats how I would do it

#

might not be the best solution though

#

I am sometimes unsure about OOP

deep cave
deep cave
#

yeah so like

#

if i have my controler

#

and i want it to switch to a different scene

#

like idk how to make it so that it uses the switchScene() method from my manager

ornate badge
#

add a manager field in the controller

deep cave
#

ahh and then pass it in every time

#

i see

ornate badge
#
class MyController implements Initializable {
  private MyManager manager;
  public void setManager(...) {
    this.manager = manager;
  }
  ...
}
ornate badge
#

set it once

deep cave
ornate badge
#

no

#

pass the same

deep cave
#

AHH OK

#

oops sos caps

#

also

#

what does the Initializable interface do?

ornate badge
#

it defines a method which is called when the controller is loaded

ornate badge
#

useful when you want to setup things

hoary pine
deep cave
#

so i don't have to do the object initialization stuff?

ornate badge
#

wdym

deep cave
#

it just does that when i load the controller?

ornate badge
#

?

deep cave
#

umm

#

i think i am confusing

#

one second lemme try the code and i could figure out myself maybe

hoary pine
#

the initialize method provided by Initializable is called by JavaFX if you load an fxml file through FXMLLoader

#

the custom made init method will be used by our Manager class

#

and called each time you would switch a scene

deep cave
#

ahh

#

so my controller class would require both?

hoary pine
#

depends what you want to do

#

but implementing Initializable is optional

#

would you use it in your controller?

deep cave
#

hmm

#

thing is i am not sure 😭

#

i am going to try implement without initializable first

#

Ahhh

#

i think i know now

#

so every controller would need a manager to switch the scenes around if needed

hoary pine
#

they should share the same Manager instance

deep cave
#

and since every scene in my program has a button which leads to another scene, they should share the same manager

deep cave
#

so i have to think of a way to give them all the same manager

#

would that be through the enum?

ornate badge
ornate badge
#

use a setter

#

and do controller.setManager(manager)

deep cave
#

ahh and i call that setter from the enum class?

#

i do get that i can use a setter but i am unsure where to call it from such that they all have the same manager

ornate badge
#

wdym

#

you can it when you initialize everything

#

probably in the start method

deep cave
#

I have to do that for every controller NootLikeThis

#

is there a way I can do it for all controllers quickly?

hoary pine
#

share your code

deep cave
#
@Override
    public void start(Stage stage) {
        Manager sceneManager = new Manager(stage);

        HomePageController controller = new HomePageController();
        controller.setManager(sceneManager);

        sceneManager.loadScene(ProgramState.MAIN_MENU);
    }``` 

this is the start method so far (I haven't put in the other controllers yet)
hoary pine
#

you shouldnt create the controller yourself

#

its provided by FXMLLoader

deep cave
#

oh πŸ’€

deep cave
#

that's where i am confused

#

is it within the program state class?

ornate badge
deep cave
#

oh no i get that

#

this is specific to the ProgramState enum i had created earlier

#

so i have these

#
public class Fluidity extends Application {

    @Override
    public void start(Stage stage) {
        Manager sceneManager = new Manager(stage);
        sceneManager.loadScene(ProgramState.MAIN_MENU);
    }

    public static void main(String[] args) {
        launch();
    }
}```
hoary pine
#

where are you using FXMLLoader?

deep cave
#

and this

public enum ProgramState {
    MAIN_MENU("/MainMenu.fxml"),
    MAIN_SIMULATION("/MainSimulation.fxml"),
    SETTINGS("/Settings.fxml"),
    ABOUT("/About.fxml"),
    EXIT("/Exit.fxml"),
    RECORDINGS("/Recordings.fxml");

    private final String path;
    private Scene scene = null;
    private Controller controller = null;


    ProgramState(String path) {
        this.path = path;
    }

    public Scene getScene() {
        try {
            if (scene == null) {
                FXMLLoader loader = new FXMLLoader(getClass().getResource(this.path));
                this.scene = new Scene(loader.load(), 1280, 720);
                this.controller = loader.getController();
            }
        } catch (Exception e) {
            System.out.println("Scene Not found: " + "\n" + e.getMessage());
            System.exit(0);
        }
        return scene;
    }
}```
#

i don't understand how i would get the manager in there

hoary pine
#

you dont but manager can take use of the enum

#

there you are storing the controller

ornate badge
hoary pine
#

yeah I would handle this differently

hoary pine
#

but I think the program needs to close anyways

#

because it cant load stuff thats 100% needed for the application to work

deep cave
#
package com.fluidity.structure;

import javafx.stage.Stage;

public class Manager {
    // The stage which the entirety of the program will be on
    private final Stage window;

    // The manager is the main class which contains the stage
    // It controls the switching between scenes
    public Manager(final Stage window) {
        this.window = window;
        window.setTitle("Fluidity - The Eulerian Fluid Simulator");
    }

    public void loadScene(ProgramState programState) {
        window.setResizable(false);
        window.setScene(programState.getScene());
        window.show();
    }
}
#

so from this class, you're saying i should access the enum where i am storing the controllers and then set manager?

hoary pine
#

programState.getController().setManager(this)

#

smth like that

deep cave
#

yeahhhh

hoary pine
#

but you maybe need instanceof and cast here

deep cave
#

oooh

#

it works

#

i will fix the try catch dw 😭

#
public Manager(final Stage window) {
        this.window = window;
        window.setTitle("Fluidity - The Eulerian Fluid Simulator");

        for (ProgramState programState : ProgramState.values()) {
            programState.initialise();
            if (!programState.isEmpty()) {
                programState.setManager(this);
            }
        }
    }```
runic osprey
#

(I would also log if scene != null)

hoary pine
#

also I wouldnt call the enum ProgramState

ornate badge
#

you are just abusing static here

#

this should be in the manager

hoary pine
#

I would make an enum for all scenes

#

but it shouldnt have all this logic

ornate badge
#

yea

runic osprey
#

Yeah I guess if you think to name your enum state, it should probably be split up to an object pepekek

#

I've been lowkey lurking on your progress through all these threads πŸ˜† Cool project

hoary pine
#

lmao

lapis spindle
#

i wouldnt recommend using enums for this, since it'll keep everything in memory & all states will be exposed - it introduces global mutable state with setManager

hoary pine
#

put the logic in Manager

deep cave
#

primarily cos i haven't read much about it and don't have time to

deep cave
hoary pine
#

yeah

#

your enum should only store the paths in this case

deep cave
hoary pine
#

yeah most likely

#

like:

// find better name here
public enum UI {
    HOME("path/to/fxml");
    
    private final String path;

    UI(String path) {
        this.path = path;
    }

    // getter
}
private final Map<UI, Scene> scenes = new EnumMap<>(UI.class); 

... logic in Manager

lapis spindle
deep cave
#

Enum Map?

#

i was gonna use a hashmap

hoary pine
lapis spindle
#

there are other factors, such as persistenting the data that was on the scene last time it was shown, or whether the scene should be reset to it's default state when shown

hoary pine
#

and got a nice way for switching scenes

#

as you can use the Enum to identify a scene (fxml)

deep cave
hoary pine
#

its not fitting though

deep cave
#

oh

hoary pine
#

I couldnt come up with a better name though

#

you cant name it Scene

#

because of javafx Scene

lapis spindle
#

the manager could cache scenes when needed. that way the amount of scenes possible are still unbounded, and you could still save time by re-using scenes

ornate badge
lapis spindle
#

without having to keep all the scenes in memory

hoary pine
#

there will be like 5 Scenes

lapis spindle
deep cave
hoary pine
deep cave
ornate badge
lapis spindle
hoary pine
#

?

#

the design will be the same

#

just the Manager would be different then

#

you wouldnt use a Map

#

but instead some Cache datastructure

lapis spindle
#

if they only expect to have a few scenes, then this doesn't matter. keeping them all in memory wont have a huge impact

ornate badge
deep cave
#
    private final Map<ProgramState, Scene> scenes = new EnumMap<>(ProgramState.class);
    private final Map<ProgramState, Controller> controller = new EnumMap<>(ProgramState.class);``` 

does creating an EnumMap mean i don't have to put every enum value into the map?
hoary pine
#

nah its just a Map implementation made for Enum type keys

ornate badge
deep cave
#

ahh

lapis spindle
hoary pine
#

loading a new scene each time switching wont be nice either

ornate badge
#

it depends of what you want to do

#

but it isn't a problem right now

hoary pine
#

yeah it really depends on the context

deep cave
#

WOOOOOO

#

IT WORKS

#

now to make it look 10000x better

hoary pine
deep cave
#

btw is it possible to set background images in css?

#

or is it done in fxml

hoary pine
#

uh both works probably

lapis spindle
lapis spindle
#

in their case, they may need only a few. but in general, you wouldn't want to define scenes with enums. it violates O/C, as you'll be constantly adding scenes as needed, unless you were 100% sure you only needed a small specific amount of scenes

hoary pine
#

How would you do it then?

ornate badge
#

It's basically the same code, but instead of caching the scenes, you either don't cache them or use weak references
But at the end, the design is exactly the same so I don't know what you are talking about

hoary pine
#

yeah thats what I thought

lapis spindle
# hoary pine How would you do it then?

i would have the manager decide which scenes to keep in memory. the more frequent scenes would stay in memory, while lesser-used scenes would be re-loaded. scenes would specify whether it's state should be persisted or not, so the manager could save a snapshot of the state in areas where the scene needs to be reconstructed with it's previous state

hoary pine
#

then you didnt understand what I mean

#

the enum is only storing the path to the .fxml file

#

the manager is loading and handling the actual scenes

lapis spindle
#

yeah, but you'd have to modify the enum anytime a new scene is introduced, and could wind up with a shit-ton of enum values

#

(that's where O/C is violated)

#

its fine for small 1-person projects

hoary pine
#

it is also fine for bigger projects

#

you will never ever reach 100 scenes

lapis spindle
#

not when multiple people are introducing scenes

lapis spindle
hoary pine
#

even if, would having 100 enum values be bad? I dont think so

lapis spindle
#

think of all the different views you encounter within apps you currently use

hoary pine
#

yeah but not everything is a new scene

lapis spindle
#

it's the fact that any new scene would require the enum class to be modified, which isn't a good idea if you decide to introduce a new developer onto the project

#

it may work for them, and i even said this design is probably fine for them, if that's their situation

ornate badge
#

swapTo(View.MAIN_MENU)

#

thanks to enums, you can do that

#

but without it

#

you are stuck with strings

lapis spindle
ornate badge
lapis spindle
#

not all scenes should be known by all code. there are nested scenes, which should only be known by their parent

ornate badge
#

Actually, the best of both worlds would be to use reflection

#

swapTo(MainController.class)

#

it gets rid of the disadvantages of both

#

but the problem of reflection... is reflection itself

lapis spindle
ornate badge
#

Question is, do you want to play with reflection is a student project pepekek

lapis spindle
#

i mean, it needs extra detail, but that would allow it to be unbounded

ornate badge
#

or small project

lapis spindle
#

it doesnt need to use reflection, though. the constants could be defined in the class, privately, to allow compile-time safety

#

the benefit is removing the bounds on how many scenes there are

#

you arent depending on some global base of scenes. each scene handles it's own dependencies

#

the manager itself decides whether to re-use scenes or re-load scenes, whether to persist state in a scene, etc..

#

it'll actually manage the scenes, opposed to being a middle-man for simply swapping between them