-1

I'm stuck on full scaling for my JavaFX application. I'm in the process of making a full screen feature for the application and I'm running into issues on trying to get the aspect ratio and positioning right without manually editing the values.

With the way I've been trying, the values butcher the game's start screen making the positioning change making the designs of the game offset from the center of the application. I can understand the reasoning behind it with the way I set it up. My problem is wondering how to scale the start screen and keep it's original position without having to manually edit the values.

The original set up

What I thought of was trying to input the value and having it scale according to that value then putting the result in the position of objects X and Y.

if (fullscreen) {
    WIDTH = (Enter aspect ratio here) * 1.5;
    HEIGHT = (Enter aspect ratio here) * 1.5;
} else {
    WIDTH = 990;
    HEIGHT = 525;
}

with Obvious flaws this butchers the start screen.

straight butchered

My solution was to make a double() that you just enter the value of the application WIDTH/HEIGHT then entering the amount you want to divide by (since I couldn't come up with exact cords, I grabbed the WIDTH and divided by specific value for it to align in the center) following with a boolean to state whether it's full screened or not. Though my only issue with this theory is that it'll only work with 1920x1080 monitors so I'd assume I would have to manually enter all types of aspect ratios to make it fit otherwise the start screen would be butchered.

I've seen a way of scaling here: JavaFX fullscreen - resizing elements based upon screen size

Though I'm not sure how to correctly implement it.

public static boolean fullscreen = false;

public static double WIDTH = 990;
public static double HEIGHT = 525;

public static Pane pane = new Pane();

public static void StartScreen() {  
        
        pane.setPrefSize(WIDTH, (HEIGHT - 25)); // the 25 is for the text field/input.
        pane.setStyle("-fx-background-color: transparent;");
        
        Group sGroup = new Group();
        
        Image i = new Image("file:start/So7AA.png");

        ImageView outer = new ImageView(i);
        
//      outer.setX(Ce.WIDTH/4.75); //4.75 // The functioning code for the snippit
//      outer.setY(-10); //-10
        
        outer.setX(Ce.WIDTH/position(3.60, fullscreen)); //4.75 // The non functioning code.
        outer.setY(position(-1, Ce.fullscreen)); //-10
        
        outer.setFitWidth(550);
        outer.setFitHeight(550);
        
        outer.setOpacity(.3);
        
        GaussianBlur gBlur = new GaussianBlur(); 
        gBlur.setRadius(50);
        
        ImageView seal = new ImageView(i);
        
//      seal.setX(Ce.WIDTH/3.83); //247.5 - 3.83
//      seal.setY(39); //39
        
        seal.setX(Ce.WIDTH/position(3.83, fullscreen)); //247.5 - 3.83
        seal.setY(position(32, Ce.fullscreen)); //39
        
        seal.setFitWidth(450);
        seal.setFitHeight(450);
        
        ImageView sealBlur = new ImageView(i);
        
//      sealBlur.setX(Ce.WIDTH/3.83); //247.5 - 3.83
//      sealBlur.setY(39); //39
        
        sealBlur.setX(Ce.WIDTH/position(3.83, fullscreen)); //247.5 - 3.83
        sealBlur.setY(position(32, Ce.fullscreen));
        
        sealBlur.setFitWidth(450);
        sealBlur.setFitHeight(450);
        
        sealBlur.setEffect(gBlur);
    
}

For getting the values of the WIDTH and HEIGHT:

public static double getWidth(double W, boolean fs) {
        if (fs) {
            return WIDTH = Screen.getPrimary().getBounds().getMaxX();
        } else {
            return WIDTH = W;
        }       
    }
    
    public static double getHeight(double H, boolean fs) {
        if (fs) {
            return HEIGHT = Screen.getPrimary().getBounds().getMaxY();
        } else {
            return HEIGHT = H;
        }
    }

I know there's a way around this, I'm just not sure how to pull it off.

jewelsea
  • 150,031
  • 14
  • 366
  • 406
Providence
  • 59
  • 5
  • 1
    Maybe see: [JavaFX fullscreen - resizing elements based upon screen size](https://stackoverflow.com/questions/16606162/javafx-fullscreen-resizing-elements-based-upon-screen-size/16608161#16608161) – jewelsea Nov 11 '22 at 03:42
  • @jewelsea I was going to comment on that but got side tracked, but thank you I'll ask questions within that post – Providence Nov 11 '22 at 05:14
  • 2
    Instead, please [edit] your question to include a [mre] that shows your revised approach. Also consider how this [example](https://stackoverflow.com/a/70312046/230513) preserves relationships when resizing. – trashgod Nov 11 '22 at 13:04
  • 1
    Use built-in layout panes to manage the layout as much as possible, instead of hard-coding sizes. The only exception to that might be non-resizable nodes, such as `ImageView` and `MediaView`; for these I recommend wrapping in a `Pane` subclass and overriding `layoutChildren()` to call `setFitWidth/Height` to achieve the desired effect. That way you can just call `stage.setFullScreen(true)` or `stage.setMaximized(true)` as needed and it should all take care of itself, regardless of the physical display dimensions. Post a [mre]. – James_D Nov 11 '22 at 13:46
  • 1
    For example, it looks like you have three images here, which you want centered over the top of each other. Just put those in a `StackPane`, with alignment set to `CENTER` (which is the default). Create an `AnchorPane`, put the `StackPane` in the `AnchorPane` and anchor it to all four sides at zero pixels. Put the label in the `AnchorPane` and anchor it to the top left (if it's three labels, put them in a `VBox` and anchor the `VBox` to the top left). Finally, put the `AnchorPane` in the center of a `BorderPane` and the `TextField` in the bottom. Don't try to do the layout yourself. – James_D Nov 11 '22 at 13:51

1 Answers1

2

I'm not sure exactly what the requirements are here, but it looks like you have three images, which you want centered, and you want them all scaled by the same amount so that one of the images fills the available space in its container. (Then, you just need to make sure its container grows to fill all the space, and you can call stage.setFullScreen(true) or stage.setMaximized(true) as needed.)

You can do this with a pretty simple custom pane that manages the layout in the layoutChildren() method:

public class ImagePane extends Region {

    private final Image image1;
    private final ImageView imageView1;
    private final Image image2;
    private final ImageView imageView2;
    private final Image image3;
    private final ImageView imageView3;

    public ImagePane(Image image1, Image image2, Image image3) {
        this.image1 = image1;
        this.image2 = image2;
        this.image3 = image3;
        imageView1 = new ImageView(image1);
        imageView2 = new ImageView(image2);
        imageView3 = new ImageView(image3);
        getChildren().addAll(imageView1, imageView2, imageView3);
    }

    @Override
    protected void layoutChildren() {
        double xScale = getWidth() / image1.getWidth();
        double yScale = getHeight() / image1.getHeight();
        double scale = Math.min(xScale, yScale);
        for (ImageView view : List.of(imageView1, imageView2, imageView3) {
            scaleAndCenter(view, scale);
        }
    }

    private void scaleAndCenter(ImageView view, scale) {
        double w = scale * view.getImage().getWidth();
        double h = scale * view.getImage().getHeight();
        view.setFitWidth(w);
        view.setFitHeight(h);
        view.relocate((getWidth()-w) / 2, (getHeight()-h) / 2);
    }
}

The rest of your layout looks something like:

Label label = new Label("Type in 'start'.\nType in 'options' for options.\n(Demo)");
TextField textField = new TextField();
ImagePane imagePane = new ImagePane(new Image(...), new Image(...), new Image(...));
AnchorPane anchor = new AnchorPane(imagePane, label);
AnchorPane.setTopAnchor(imagePane, 0.0);
AnchorPane.setRightAnchor(imagePane, 0.0);
AnchorPane.setBottomAnchor(imagePane, 0.0);
AnchorPane.setLeftAnchor(imagePane, 0.0);
AnchorPane.setTopAnchor(label, 5.0);
AnchorPane.setLeftAnchor(label, 5.0);

BorderPane root = new BorderPane();
root.setCenter(anchor);
root.setBottom(textField);

Now everything should just respond to whatever size is assigned to the root pane, so setting full screen mode should "just work".

James_D
  • 201,275
  • 16
  • 291
  • 322
  • I'll def look into this, it seems you got an idea here. for context which you pointed out, all the ```imageView``` elements all contain the same image, just scaled and/or have an effect onto them. I will have to out both methods that were given. Thank you though, I will keep you updated! – Providence Nov 12 '22 at 03:28
  • You helped out a lot and I wanted to say thank you! Not wanting to milk it too much but can this be done with other elements such as text and rectangles besides just Images? @James_D – Providence Nov 12 '22 at 06:47
  • @Providence Sure; the method calls would be a little different though. With `Shape` subclasses you would just change their size (eg `setWidth()` and `setHeight()` for a `Rectangle`). For `Text` you probably need to do a little more work to figure out the correct font size. There are some examples of that elsewhere on this site. – James_D Nov 12 '22 at 13:55
  • Hey, @James_D I wanted to ask another question with this code. Having this work for images, text, and shapes. Does this same ```layoutChildren``` method work with elements such as ```VBox``` and ```TextField```? – Providence Nov 22 '22 at 06:01