0

I want to have a window containing two panels in proportion 3:1 vertically and have the bigger one contain a square panel inside. Additionally I'd like to draw something in the center of the square panel, say circle.

The behavior of my program suggests there is a conflict between the 3:1 proportion and the squareness. While resizing, the shapes are jumping oddly. The end result is incorrect, the first proportion does not hold. In addition the circle is off the center.

I am new to java, therefore I would appreciate any remarks on both what is wrong and how to implement this the correct way.

The result looks like:

enter image description here

I wanted to have: (1) red panel as a square (2) blue panel to be strictly 3 times higher than the green one (3) to have circle in the middle of the red box.

Here is my code:

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


public class ProportionalPanels
{
    private JFrame window = new JFrame("Proportional resizable panels");
    private JPanel allContaining = new JPanel( new GridBagLayout() );
    private JPanel upper = new JPanel( new GridBagLayout() );
    private JPanel lower = new JPanel();
    private JPanel square = new JPanel()
    { 
        @Override
        public Dimension getPreferredSize() {
            Dimension d = super.getPreferredSize();
            Container c = getParent();
            if (c != null) {
                d = c.getSize();
            } else {
                return new Dimension(10, 10);
            }
            int w = (int) d.getWidth();
            int h = (int) d.getHeight();
            int s = (w < h ? w : h);
            return new Dimension(s, s);
        }

        @Override
        protected void paintComponent(Graphics g)
        {
            super.paintComponent(g);
            g.setColor(Color.GRAY);
            int x = this.getWidth(), y = this.getHeight(); 
            g.fillOval(x/2,y/2,x/4,y/4);
        }
    };

    public ProportionalPanels()
    {
        // setting colors
        allContaining.setBackground(Color.WHITE);       
        square.setBackground(Color.RED);
        upper.setBackground(Color.BLUE);
        lower.setBackground(Color.GREEN);

        // setting upper
        upper.add(square);

        // setting allContaining
        GridBagConstraints g = new GridBagConstraints();
        g.gridx = g.gridy = 0;
        g.weightx = 1; g.weighty = 0.75;
        g.fill = GridBagConstraints.BOTH;
        allContaining.add(upper,g);
        g.gridy += 1;
        g.weighty = 0.25;
        g.fill = GridBagConstraints.BOTH;
        allContaining.add(lower,g);


        window.add(allContaining);

        // setting window
        window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        window.setLocationByPlatform(true);
        window.pack();
        window.setSize(400,200);
        window.setVisible(true);
    }

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater( new Runnable()
        {
            public void run()
            {
                new ProportionalPanels();
            }
        });
    }
}

EDIT

Imagine a simple gui for chess, only for the sake of analogy. A window is spit into two panels, upper one (blue) is to contain the chessboard, the lower one (green) some buttons. The only constraint for these panels is to keep the vertical proportion - the upper one is to be 3x higher than the lower one. Then, going deeper into panel hierarchy, the upper panel contains square panel (red), which is a square, has the height of the upper panel and is horizontally placed in the middle of the upper panel. Extra space is on the left and right of the square panel, adjusts accordingly to keep red panel square and green's height 3 times smaller than the red's. Red panel holds some drawings at its center, here a gray circle, but to keep the chess analogy it can be an image of chessboard or anything else.

infoholic_anonymous
  • 969
  • 3
  • 12
  • 34
  • 1
    You first problem is screwing with the preferred sizes of the components and not letting the layout managers deal with it...Remember, layout managers are free to ignore these values... – MadProgrammer Nov 14 '14 at 04:27
  • 1
    In addition to the comment of @MadProgrammer, see [Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?](http://stackoverflow.com/q/7229226/418556) (Yes.) – Andrew Thompson Nov 14 '14 at 04:30
  • When a layout manager wants to determine the best size for it's requirements, it's normally doing so because the parent size is unknown, you'd rely on using measurements from the parent when calculating your preferred size (and shouldn't), you should simply be saying, "I'd like to be this size if at all possible please"... – MadProgrammer Nov 14 '14 at 04:31
  • *"I would appreciate remarks on both what is incorrect and.."* That information should be **in the question,** rather than as a comment below it. – Andrew Thompson Nov 14 '14 at 04:42
  • @MadProgrammer you mean my overriding of `getPreferredSize`? I took it from http://stackoverflow.com/a/16075907/1342976 Could you suggest then how should that be done in a correct way? – infoholic_anonymous Nov 14 '14 at 04:44
  • Yep, write your own layout manager that does what you want...:P – MadProgrammer Nov 14 '14 at 04:46
  • @MadProgrammer I've read one of your layouts, I like the idea. But, as I am just starting with Java, I am curious if that is a standard way of doing this? I mean, what I'm attempting to do seemed pretty basic, does it really require that much coding? – infoholic_anonymous Nov 14 '14 at 04:53

1 Answers1

2

I'm not 100% sure I understand the question, however, it's never stopped me before...

Basically, this uses a VERY crude, custom layout manager. It will keep the first and last components sized do 3:1 ratio (width is ratio of the height)

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.LayoutManager;
import java.awt.LayoutManager2;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
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();
                }

                JPanel master = new JPanel(new PropertionalLayoutManager());
                master.add(new TestPane(), "left");
                master.add(new DrawPane(), "center");
                master.add(new TestPane(), "right");

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(master);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class PropertionalLayoutManager implements LayoutManager {

        private Component left;
        private Component right;
        private Component center;

        @Override
        public void addLayoutComponent(String name, Component comp) {
            if ("center".equals(name)) {
                center = comp;
            } else if ("left".equals(name)) {
                left = comp;
            } else if ("right".equals(name)) {
                right = comp;
            }
        }

        @Override
        public void removeLayoutComponent(Component comp) {
        }

        public Dimension getSize(Dimension leftSize, Dimension centerSize, Dimension rightSize) {


            Dimension size = new Dimension();
            if (leftSize != null && right != null) {

                int width = leftSize.width;
                int height = leftSize.height;

                size.width = Math.max(width, rightSize.width) * 2;
                size.height = Math.max(height, rightSize.height);

            } else if (leftSize != null) {

                size.width = leftSize.width;
                size.height = leftSize.height;

            } else if (right != null) {

                size.width = rightSize.width;
                size.height = rightSize.height;

            }

            if (center != null) {

                size.width += centerSize.width;
                size.height = Math.max(centerSize.height, size.height);

            }

            return size;

        }

        @Override
        public Dimension preferredLayoutSize(Container parent) {

            Dimension leftSize = left == null ? null : left.getPreferredSize();
            Dimension centerSize = right == null ? null : right.getPreferredSize();
            Dimension rightSize = center == null ? null : center.getPreferredSize();

            return getSize(leftSize, centerSize, rightSize);

        }

        @Override
        public Dimension minimumLayoutSize(Container parent) {

            Dimension leftSize = left == null ? null : left.getMinimumSize();
            Dimension centerSize = right == null ? null : right.getMinimumSize();
            Dimension rightSize = center == null ? null : center.getMinimumSize();

            return getSize(leftSize, centerSize, rightSize);

        }

        @Override
        public void layoutContainer(Container parent) {

            // Get rid of anything else that might have been added...
            for (Component comp : parent.getComponents()) {

                comp.setBounds(0, 0, 0, 0);

            }

            int width = parent.getWidth();
            int height = parent.getHeight();

            int outterWidth = height / (3 / 1);

            if (left != null) {

                left.setBounds(0, 0, outterWidth, height);

            }

            if (right != null) {

                right.setBounds(width - outterWidth, 0, outterWidth, height);

            }

            if (center != null) {

                center.setBounds(outterWidth, 0, width - (outterWidth * 2), height);

            }

        }

    }

    public class DrawPane extends JPanel {

        public DrawPane() {
            setBackground(Color.BLUE);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(100, 300);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            int x = (getWidth() - (getWidth() / 4)) / 2;
            int y = (getHeight()- (getHeight() / 4)) / 2;
            g.setColor(Color.GRAY);
            g.fillOval(x, y, getWidth() / 4, getHeight() / 4);
        }

    }

    public class TestPane extends JPanel {

        private JLabel size;

        public TestPane() {
            setBackground(Color.GREEN);
            setLayout(new GridBagLayout());
            size = new JLabel();
            addComponentListener(new ComponentAdapter() {
                @Override
                public void componentResized(ComponentEvent e) {
                    size.setText(getWidth() + "x" + getHeight());
                }
            });
            add(size);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(100, 300);
        }

    }

}

Updated

Something more like ...

SquareSquare

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
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();
                }

                JPanel master = new JPanel(new SquareLayoutManager());
                master.setBackground(Color.BLUE);
                master.add(new DrawPane());

                JPanel green = new JPanel();
                green.setBackground(Color.GREEN);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new GridBagLayout());

                GridBagConstraints gbc = new GridBagConstraints();
                gbc.gridx = 0;
                gbc.gridy = 0;
                gbc.weightx = 1;
                gbc.weighty = 0.66666666666667;
                gbc.fill = GridBagConstraints.BOTH;
                frame.add(master, gbc);

                gbc.gridy++;
                gbc.weighty = 0.33333333333333;
                frame.add(green, gbc);

                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class SquareLayoutManager implements LayoutManager {

        @Override
        public void addLayoutComponent(String name, Component comp) {
        }

        @Override
        public void removeLayoutComponent(Component comp) {
        }

        @Override
        public Dimension preferredLayoutSize(Container parent) {

            int width = 0;
            int height = 0;
            for (Component comp : parent.getComponents()) {
                width = Math.max(comp.getPreferredSize().width, width);
                height = Math.max(comp.getPreferredSize().width, height);
            }

            // You could define rows and columns and blah, blah, blah, but I'm to lazy...

            return new Dimension(Math.max(width, height) * parent.getComponentCount(), Math.max(width, height));

        }

        @Override
        public Dimension minimumLayoutSize(Container parent) {

            return preferredLayoutSize(parent);

        }

        @Override
        public void layoutContainer(Container parent) {

            Insets insets = parent.getInsets();
            int size = (parent.getHeight() - (insets.bottom + insets.top));
            int x = (parent.getWidth() - (insets.left + insets.right) - size) / 2;
            int y = insets.top;
            for (Component comp : parent.getComponents()) {
                comp.setBounds(x, y, size, size);
                x += size; // Could define a gap, but, lazy...
            }

        }

    }

    public class DrawPane extends JPanel {

        public DrawPane() {
            setBackground(Color.RED);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(100, 300);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            int x = (getWidth() - (getWidth() / 4)) / 2;
            int y = (getHeight() - (getHeight() / 4)) / 2;
            g.setColor(Color.GRAY);
            g.fillOval(x, y, getWidth() / 4, getHeight() / 4);
        }

    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • I get an empty window when running it under Debian Linux. – infoholic_anonymous Nov 14 '14 at 06:12
  • Wow, really...What version of Java...(and you run my example, unmodified?) – MadProgrammer Nov 14 '14 at 06:14
  • Well, you'll be glad to know, it did exactly what it should have done, not what we wanted it to do. I've updated the example, it should now display something... – MadProgrammer Nov 14 '14 at 06:19
  • I updated my question to be clear what I want to obtain. In your solution no panel is a square actually. – infoholic_anonymous Nov 14 '14 at 06:34
  • GBC (always) or MigLayout – mKorbel Nov 14 '14 at 08:01
  • No, I was taking the 3:1 ratio meaning you wanted the panel to be 1 wide by 3 high, which panels should be square? – MadProgrammer Nov 14 '14 at 08:05
  • @MadProgrammer see description under the image I posted. the red panel, named `square`, must be square and have a circle in the middle. it is contained in `upperPanel` (blue panel), which vertical dimension is 3 times greater than `lowerPanel`'s (green panel). – infoholic_anonymous Nov 14 '14 at 20:17
  • Where does the extra space go? In what direction does the red panel give bias, width or height of parent? – MadProgrammer Nov 14 '14 at 21:22
  • @MadProgrammer see my edit to the question placed at the bottom, I've just made it as an answer for your question. red panel takes its size from the height of the upper panel, which in turn is to be 3/4 of the window height. – infoholic_anonymous Nov 15 '14 at 00:19
  • @MadProgrammer great! thanks a lot, that's what I wanted. I have couple of questions though: (1) the green and blue panel do resize along with the window, but the proportion doesn't strictly hold at all times; at the initial state blue one is much bigger than the defined 0.666.. of the window height, why? (2) the circle does lie perfect, but why is getWidth() / 4 needed in the paintComponent method? I don't get it. (3) why is there a tiny white line under green panel? Is it an inherent GridBagLayout issue? – infoholic_anonymous Nov 15 '14 at 06:27
  • 1- This will be effected by the panels preferred and minimum sizes. I'd be tempted to set these values within the green panel by overriding them, this should force the `GridBagLayout` to maintain them. 2- Because I wanted the circle to be a 1/4 of the width/height of it's parent container. 3- I think that's just MacOs been annoying (having lots of fun with Yosemite)... – MadProgrammer Nov 15 '14 at 06:32
  • @MadProgrammer (1) get it, thanks (2) Ohhh, of course. I thought the placement of a circle is defined by declaration of its center, hence the stupid question, sorry. (3) No, I have it as well on my Debian Linux. But it's a minor issue. || Thank you very much for your quality help, time and patience! – infoholic_anonymous Nov 15 '14 at 07:34