0

For some reason, the JFrame is too small when run, and using pack(); doesn't seem to fix the problem. Any ideas?

   public class Window {

    public Window(String title, Game game) {

        // Creates new JFrame
        JFrame frame = new JFrame(title);

        // Adds Game to window
        frame.add(game);
        frame.pack();   

        // Settings of Window
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

    }

}

Screenshot of the program.

The cyan background is supposed to be a 64 x 64 border in the bottom and right sides. The boxes drawn are correct, which I checked by resizing the window. However the window is too small and does not fit the canvas.

Game Class:

public class Game extends Canvas implements Runnable {

private Handler handler;

public Game() {

    // Create new window
    Window window = new Window("Horses Aren't Real", this);

    handler = new Handler();
    this.addKeyListener(new KeyInput(handler));

    handler.addObject(new Daanish(100, 100, ID.Player, handler));

}

public static void main (String args[]) {

    // Creates new game
    new Game();

}

public void draw() {

    // Creates a new BufferStrategy
    BufferStrategy bs = this.getBufferStrategy();

    if (bs == null) {

        // This allows the game to preload 3 frames in order to prevent choppy framrate
        this.createBufferStrategy(3);

        return;

    }

    // Create Graphics
    Graphics g = bs.getDrawGraphics();

    g.setColor(Color.cyan);
    g.fillRect(0, 0, 1024, 640);

    g.setColor(Color.black);
    g.fillRect(0, 0, 960, 576);

    handler.draw(g);

    // Remove frame from queue
    g.dispose();
    bs.show();

}

@Override
public Dimension getPreferredSize() {

    return new Dimension(1024, 640);

}

}
FudgeMuffins
  • 321
  • 2
  • 12

2 Answers2

2

So you're running into a number of issues. The first been, the size of the window must also account for the frame decorations, this means that the available space for the content is the size of the window MINUS the size of the frame decorations.

Setting the size of the window directly is ill-advised.

pack asks the content of the frame for it's preferred size and uses that to wrap the window around it. So, instead of window size - decorations = content size, you have content size + decorations = window size

From the JavaDocs

Causes this Window to be sized to fit the preferred size and layouts of its subcomponents. The resulting width and height of the window are automatically enlarged if either of dimensions is less than the minimum size as specified by the previous call to the setMinimumSize method.

So, instead, override preferredSize of the Game class and return the amount of space you want, may be something like...

public class Game extends JPanel {
    //...
    @Override
    public Dimension getPreferredSize() {
        return new Dimension(800, 600);
    }
    //...
}

Your next problem is, Window extends from JFrame, but in the constructor, you're creating ANOTHER Frame ... so which one is actually doing what?

Remove the confusion. Avoid extending from top level containers like JFrame, you're not adding any new functionality to it

public class Window {

    public Window(String title, Game game) {

        // Creates new JFrame
        JFrame frame = new JFrame(title);

        // Adds Game to window
        frame.add(game);
        frame.pack();

        // Settings of Window
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

    }

}

Updated...

So, I took your "updated" code, modified it so it would run, ran and didn't have any issues...

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                Game game = new Game();
                Window window = new Window("Help", game);

                game.start();
            }
        });
    }

    public class Window {

        public Window(String title, Game game) {

            // Creates new JFrame
            JFrame frame = new JFrame(title);

            // Adds Game to window
            frame.add(game);
            frame.setResizable(false);
            frame.pack();

            // Settings of Window
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);

        }

    }

    public class Game extends Canvas { //implements Runnable {

//      private Handler handler;

        public Game() {
//
//          handler = new Handler();
//          this.addKeyListener(new KeyInput(handler));
//
//          handler.addObject(new Daanish(100, 100, ID.Player, handler));

        }

        public void start() {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        draw();
                        try {
                            Thread.sleep(40);
                        } catch (InterruptedException ex) {
                        }
                    }
                }
            });
            thread.start();
        }

        public void draw() {

            // Creates a new BufferStrategy
            BufferStrategy bs = this.getBufferStrategy();

            if (bs == null) {
                // This allows the game to preload 3 frames in order to prevent choppy framrate
                this.createBufferStrategy(3);
                return;
            }

            // Create Graphics
            Graphics g = bs.getDrawGraphics();

            g.setColor(Color.cyan);
            g.fillRect(0, 0, 1024, 640);

            g.setColor(Color.black);
            g.fillRect(0, 0, 960, 576);

//          handler.draw(g);

            // Remove frame from queue
            g.dispose();
            bs.show();

        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(1024, 640);
        }

    }

}

However, I'm running on MacOS. There is a little known issue with setResizable I've run across on Windows, where the window decorations change size when setResizable is called.

The "basic" solution is to call setResziable BEFORE calling pack, so that the frame decorations are modified before the the API makes decisions about how to update the size of the window.

I had thought this was fixed in later (8+) versions of Java, but since I don't run Windows, it's impossible for me to test

See My JFrame always becomes a few pixels too big. for some more details.

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • If I use the code you suggested, the window is now slightly bigger than the dimensions set. Why is this? – FudgeMuffins Sep 30 '18 at 01:11
  • 1
    @FudgeMuffins *"Causes this Window to be sized to fit the preferred size and layouts of its subcomponents*" - *"pack asks the content of the frame for it's preferred size and uses that to wrap the window around it. So, instead of window size - decorations = content size, you have content size + decorations = window size "* – MadProgrammer Sep 30 '18 at 01:12
  • @FudgeMuffins [This may also help to explain - it has pictures](https://stackoverflow.com/questions/13457237/how-to-get-the-exact-middle-of-a-screen-even-when-re-sized/13460914#13460914) – MadProgrammer Sep 30 '18 at 01:13
  • Yes, however the biggest size is `(1024, 640).` Isn't there a way to have the dimensions exactly that size? – FudgeMuffins Sep 30 '18 at 01:13
  • @FudgeMuffins Not reliably. As AndrewThompson pointed out, different platforms have different sized decorations. You application shouldn't be concerned with how big the window is, only how big the area it has available. Also consider that on Windows systems (especially), it's possible to change the font size of the title bars (for example), which will in turn change the available content size or window size, depending on which approach you use. Better to discard it and focus solely on the size of the content – MadProgrammer Sep 30 '18 at 01:16
  • Yeah that's what I meant. The content size should be 1024 x 640. – FudgeMuffins Sep 30 '18 at 01:20
  • @FudgeMuffins Yes, so define it in the context of the `Game` class via the `getPreferredSize` method. The windows decorations will then be added around it, which will make the window slightly larger – MadProgrammer Sep 30 '18 at 01:21
  • I mean that [this](https://imgur.com/a/43cOuoJ) shouldn't be there. Is that fixable? – FudgeMuffins Sep 30 '18 at 01:32
  • @FudgeMuffins Since the code I tested the above with works, then you'll need to provide more context before we can solve the problem – MadProgrammer Sep 30 '18 at 01:33
  • I edited the question to include as much relevant code as possible. – FudgeMuffins Sep 30 '18 at 01:39
1
public class Window extends JFrame {

public Window(final int width, final int height, String title, Game game) {
    super(title);
    // Set dimensions
    Dimension d = new Dimension(width, height);
    game.setPreferredSize(d);

    // Adds Game to window
    add(game);
    // Settings of Window
    setResizable(false);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setLocationRelativeTo(null);
    pack();
    setVisible(true);

}

}

When you use pack, the frame generaly resize to content size

Mead
  • 100
  • 10
  • Used the code you proposed, same result. The window is still too small. – FudgeMuffins Sep 30 '18 at 00:54
  • The new version worked, but can you explain why adding `game.setPreferredSize(d);` works? – FudgeMuffins Sep 30 '18 at 01:00
  • When you use pack, the frame generaly resize to content size (here game). You can see the frame like a bag in swing (don't know if you see what I mean ^^') – Mead Sep 30 '18 at 01:01
  • [Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?](https://stackoverflow.com/questions/7229226/should-i-avoid-the-use-of-setpreferredmaximumminimumsize-methods-in-java-swi) – MadProgrammer Sep 30 '18 at 01:05
  • - Should I completely avoid the use of those methods? - Yes for application code. (it's not very clear, is there a more logical reason ?) – Mead Sep 30 '18 at 01:06
  • 1
    @Mead Perfect practice makes perfect - even in "personal" projects you should endeavour to follow good practices, it prevents you from becoming lazy when it counts and since the OP obviously lacks experience with the API, it would be advisable to demonstrate good practices ... otherwise we're going to be hearing from them again soon and we'll have to explain they were given bad advice previously – MadProgrammer Sep 30 '18 at 01:08
  • @Mead Because if I have to undo one more developers misuse of `setPreferredSize` I'm going to go insane. It's the single major cause of layout issues I have to fix in production code. Modern GUIs have contend with any number of variables which will change the size requirements of the UI, even across like platforms. If done correctly, the UI should adapt to almost all of these needs. Also, you might created the perfect UI, runs brilliantly, then some slob comes along and calls `setPreferredSize` and suddenly it's crap and it's got your name all over it ... :/ – MadProgrammer Sep 30 '18 at 01:24