0

I am making an editor for a game i am creating, and i have problems implementing a scroll-able level-overview. The overview is supposed to have this layout :

enter image description here

The Canvas is supposed to always have a size of 1280x720, so i want to use a JScrollPane to be able to still view the entire canvas when the JFrame gets smaller. Now to achieve this i used a GridBagLayout and set a preferred size on the canvas. The problem is that expanding the JFrame will eventually allow you to expand the canvas beyond its preferred size. To stop this i set the layout of the scrollpanes viewport to a FlowLayout to prevent it from resizing the view its holding. This seems to create two problems:

1 : The GridBagLayout no longer distributes the space correctly when expanded to full screeen.

Problem1

2 : When you make the ScrollPane smaller than its viewport view, the view gets cut off, also scrolling doesn't seem to work.

enter image description here

To Summarize my Questions :

Is there another/better way to implement this UI, and if not how can i solve the issues of the gridbaglayout not distributing space correctly when expanded to fullscreen and cutting off the viewports view ?

Here the code i used for the examples :

public class Main extends JFrame {

public Main() {
    this.setSize(800, 400);
    this.setMinimumSize(new Dimension(400, 400));
    this.setLocationRelativeTo(null);

    JPanel optionsPanel = new JPanel();
    optionsPanel.setBackground(Color.red);

    JPanel scrollContentPane = new JPanel();
    scrollContentPane.setPreferredSize(new Dimension(700,700));
    scrollContentPane.setBorder(BorderFactory.createLineBorder(Color.red));

    JScrollPane scrollPane = new JScrollPane();
    scrollPane.getViewport().setLayout(new FlowLayout());
    scrollPane.getViewport().setView(scrollContentPane);
    scrollPane.setHorizontalScrollBarPolicy(scrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
    scrollPane.setVerticalScrollBarPolicy(scrollPane.VERTICAL_SCROLLBAR_ALWAYS);


    JPanel contentPanel = new JPanel();
    contentPanel.setLayout(new GridBagLayout());

    GridBagConstraints gc = new GridBagConstraints();
    gc.fill = gc.BOTH;
    gc.weightx = 1.0;
    gc.weighty = 1.0;

    gc.gridx = 0;
    gc.gridy = 0;
    contentPanel.add(optionsPanel, gc);

    gc.gridx = 1;
    contentPanel.add(scrollPane, gc);

    this.setContentPane(contentPanel);
    this.setVisible(true);

}

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

}

Here is the actual code i am using in my programm :

    previewPanel = new PreviewPanel(this);
    controlPanel = new ControlPanel(this);

    centerPanel = new JPanel();
    centerPanel.setLayout(new GridBagLayout());

    GridBagConstraints c = new GridBagConstraints();
    c.fill = c.BOTH;
    c.weightx = 1;
    c.weighty = 1;

    // #HACK, fill grids with empty labels
    c.gridx = 0;
    c.gridy = 0;
    centerPanel.add(new JLabel(), c);

    c.gridx = 1;
    centerPanel.add(new JLabel(), c);

    c.gridx = 2;
    centerPanel.add(new JLabel(), c);

    c.gridx = 3;
    centerPanel.add(new JLabel(), c);

    c.gridx = 0;
    c.gridy = 0;
    c.gridwidth = 1;

    centerPanel.add(controlPanel, c);

    c.gridx = 1;
    c.gridy = 0;
    c.gridwidth = 3;

    centerPanel.add(previewPanel, c);


    setLayout(new BorderLayout());
    add(centerPanel, BorderLayout.CENTER);
    add(menuBar, BorderLayout.NORTH);
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Kujoen
  • 173
  • 1
  • 8
  • `when expanded the gridbaglayout no longer distributes the space correctly.` - define correctly? The GridBagLayout first gives each component its preferred size. Then if there is extra space available, that space is divided based on your weights, in this case 50/50. – camickr Mar 20 '18 at 17:15
  • Correctly as in i expect it to only take up half of the screen, since i use only two grid cells. You can see in the screenshot though it takes up more, how can i prevent that ? – Kujoen Mar 20 '18 at 17:18

2 Answers2

2

Don't play with the layout manager of the viewport

If you want to control the size of the panel added to the viewport, then add your panel to a wrapper panel instead. Something like:

JScrollPane scrollPane = new JScrollPane();
//scrollPane.getViewport().setLayout(new FlowLayout());
//scrollPane.getViewport().setView(scrollContentPane);
JPanel wrapper = new JPanel();
wrapper.add( scrollContentPane );
scrollPane.getViewport().setView(wrapper);
camickr
  • 321,443
  • 19
  • 166
  • 288
2

Border cut off; this is caused because the JScrollPane doesn't recognize the border as a part of the component. So when you scroll all the way to the edge you will see the first non border pixel of your component. To solve this instead of setting layout on the viewport create another panel add the canvas to this new panel and set this new panel as the viewport in your scroll pane.

JPanel scrollContentPane = new JPanel();
// to set the size properly see later
JPanel scrollContentPaneViewport = new JPanel(new FlowLayout());
scrollContentPaneViewport.add(scrollContentPane);
JScrollPane scrollPane = new JScrollPane();
scrollPane.getViewport().setView(scrollContentPaneViewport);

To restrict the size of your canvas (scrollContentPane) you shouldn't set sizing hints set[minimum|preferred|maximum]size (reason is here) instead you should subclass the panel and override the get[minimum|preferred|maximum]size methods, override the ones you need but if you want to make the panel always fixed size you should override all three.

JPanel scrollContentPane = new JPanel() {
    // this is important never return this dimension always make a copy
    private final Dimension d = new Dimension(700, 700);

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(d);
    }
    @Override
    public Dimension getMinimumSize() {
        return new Dimension(d);
    }
    @Override
    public Dimension getMaximumSize() {
        return new Dimension(d);
    }
};

To make sure that the optionsPanel and the scrollPane are always equally divided use GridLayout instead of GridBagLayout.

Edit: GridBagLayout component relative distribution

You should provide the layout with information on what to do with the extra space. This is done by setting the rowWeights and columnWeights fields.

JPanel contentPanel = new JPanel();
GridBagLayout gbl_contentPanel = new GridBagLayout();
// This tells the layout that first row should take all available space (there is only one row)
gbl_contentPanel.rowWeights = new double[]{1.0};
// This tells the layout that second column should be 3 times bigger than first column (3 is 3 times bigger than 1)
gbl_contentPanel.columnWeights = new double[]{1.0, 3.0};
contentPanel.setLayout(gbl_contentPanel);

This should tell the GridBagLayout how to distribute extra space but it still won't work.

It is because GridBagLayout is one of the layouts that respects preferred size returned from components. The two components you added are "telling" the layout their minimum maximum and preferred size and layout takes it into account when calculating how much space the components should get.

To solve that you should override getPreferredSize() in both components to return equal dimensions. (I like to return 0, 0)

JPanel optionsPanel = new JPanel() {
    @Override
    public Dimension getPreferredSize() {
        return new Dimension();
    }
};
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
MatheM
  • 801
  • 7
  • 17
  • This solves the issue of the cut-off border nicely. The only remaining issue is that when expanded the gridbaglayout no longer distributes the space correctly. – Kujoen Mar 20 '18 at 17:03
  • How do you want to distribute the space? Half and half? Half for options and half for scroll pane? In that case simple `GridLayout` would be better. – MatheM Mar 20 '18 at 17:23
  • In retrospect i have chosen a bad example. In my actual UI, the space is distributed in fourths, meaning the "canvas" Panel takes up 3/4 of the space and the options 1/4. The problem is still the same, that the options part gets smushed when you expand the frame too much. – Kujoen Mar 20 '18 at 17:27
  • That changes things, I know what is causing the issue I will edit the answer. – MatheM Mar 20 '18 at 17:30
  • I have added the actual code i am using to my question – Kujoen Mar 20 '18 at 17:31
  • Dang it, you keep editing the question. So now you want 4 equal columns stretch across entire width, one row big enough for a label, second row to fill all remaining space, each column should start with a JLabel and below them in first column should be a jpanel and next to it in remaining three columns should be scrollPane? Is that correct? – MatheM Mar 20 '18 at 18:04
  • `I have added the actual code i am using to my question` - that doesn't help us. Post a proper [mcve] that demonstrates the problem. And we don't have access to your ControlPanel or PreviewPanel, so the `MCVE` should simulate the preferred size of these panelsThis should be done in a new question since you already have and answer for the original question. – camickr Mar 20 '18 at 18:31
  • MatheM that is not correct, I want one row with four columns, with one JPanel taking one column and another Jpanel the other 3. I am adding 4 JLabels because otherwise gridwidth would not work (which is an known issue). Overriding setPrefferedSize to 0,0 has the sideeffect that the "Canvas" panel doesn't show up in the ScrollPane anymore (since its size is 0,0). @camickr, the code I added was just to demonstrate to MatheM why i cannot use a gridlayout, your "Minimal complete and verifiable example" is already in my question. – Kujoen Mar 20 '18 at 20:50
  • 1
    You shouldn't add components just to add columns, use `rowWeights` / `rowHeights` and `columnWeights` / `columnWidths` to define amount of rows and columns in the layout. If you need only two components side by side you shouldn't need to set `gridwidth` because you should be using only 2 columns. Setting `columnWeights` tells the layout how to divide space. Anyways this answer is getting way too long you should finalize what you have and make a new question about dividing space in `GridBagLayout`. – MatheM Mar 20 '18 at 21:07
  • @Soliture, `"Minimal complete and verifiable example" is already in my question.` - no it isn't. In one part of the question you have two components. In another you have 4 components in 4 columns. You question is confusing which is why you have been asked for a proper [mcve] in a new question if you want help. – camickr Mar 21 '18 at 00:39