Changing JLabel text changes distribution of space by GridBagLayout

48 Views Asked by At

I have a JFrame with GridBagLayout, which contains a JLabel and a JScrollPane. The JScrollPane contains a ScrollablePanel, which in turn contains a certain amount of DebugComponents. The DebugComponents maintain an aspect ratio that is derived from the height of the JScrollPane. The layout of the JFrame with the GridBagLayout fits well at the beginning.

Now, however, I use a MouseMotionListener that changes the text of the JLabel indirectly via a thread. If the mouse is over the DebugComponent, there is a text displayed. Otherwise the area is empty. When I move to the DebugComponent, however, the layout changes. If I move the mouse back and forth between DebugComponent and ScrollablePanel, the space used by the JScrollPane gets bigger and bigger. It only stops when there is only space for the text for the JLabel left.

The code is an abstraction of my problem. Except for the class for the ScrollablePanel (get it here), the code is complete.

package de.franken.debug;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;

public class DebugGUI {

    private static JLabel label;
    public static ScrollablePanel panel;

    /** Main method + GUI configuration */
    public static void main(String[] args) {
        JFrame frame = new JFrame(); // JFrame
        frame.setLayout(new GridBagLayout());
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        GridBagConstraints c = new GridBagConstraints();

        panel = new ScrollablePanel(new GridLayout(1, 0)); // Panel containing DebugItems
        panel.setScrollableHeight(ScrollablePanel.ScrollableSizeHint.FIT);
        for (int i = 0; i < 1; i++) {
            panel.add(new DebugComponent(0.2));
        }
        c = new GridBagConstraints();
        c.gridy = 1;
        c.weighty = 0.6;
        c.gridwidth = 1;
        c.weightx = 1;
        c.fill = GridBagConstraints.BOTH;

        JScrollPane pane = new JScrollPane(panel, JScrollPane.VERTICAL_SCROLLBAR_NEVER,
                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); // ScrollPane containing Panel
        new SlideThread(pane);
        frame.add(pane, c);

        label = new JLabel("PLACEHOLDER"); // Label displaying String
        label.setHorizontalAlignment(SwingConstants.CENTER);
        c = new GridBagConstraints();
        c.gridy = 2;
        c.weighty = 0.4;
        c.fill = GridBagConstraints.BOTH;
        c.gridwidth = 1;
        c.weightx = 1;
        frame.add(label, c);

        frame.setSize(400, 400);
        frame.setVisible(true);

    }

    /**
     * Change text of JLabel beneath ScrollablePane. -> Called by SlideThread
     * 
     * @param strg : the String to be displayed
     */
    public static void setPreview(String strg) {
        label.setText(strg);
    }

    static class DebugComponent extends JComponent {

        private static final long serialVersionUID = -4770356342749347317L;
        private double widthRatio;

        /**
         * JComponent as part of ScrollablePanel with constant dimension ratio
         * 
         * @param widthRatio : the ratio to be multiplied with height to get width
         */
        DebugComponent(double widthRatio) {
            this.widthRatio = widthRatio;
        }

        @Override
        public Dimension getPreferredSize() {
            Dimension parent = super.getSize();

            return new Dimension((int) (parent.height * widthRatio), parent.height);
        }

        @Override
        protected void paintComponent(Graphics g) {
            // TODO Auto-generated method stub
            super.paintComponent(g);
            g.setColor(Color.RED);
            g.fillRect(0, 0, (int) getPreferredSize().getWidth() - 1, (int) getPreferredSize().getHeight() - 1);
        }
    }

    static class SlideThread extends Thread {
        private String strg = " ";

        SlideThread(JScrollPane pane) {
            pane.addMouseMotionListener(adapter);
            this.start();
        }

        @Override
        public void run() {
            while (true) {
                try {
                    sleep(45);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                DebugGUI.setPreview(strg);
            }
        }

        // Show String during hovering
        MouseAdapter adapter = new MouseAdapter() {

            @Override
            public void mouseMoved(MouseEvent e) {
                Component c = DebugGUI.panel.getComponentAt(e.getPoint());
                if (c instanceof DebugComponent) {
                    strg = "Titel";
                } else {
                    strg = " ";
                }
            }
        };
    }

}

1

There are 1 best solutions below

1
MarsAtomic On

GridBagLayout accounts only for visible components, so when your JLabel is blank, the layout treats it as a no-dimension component and acts as if it doesn't actually exist.

There are a couple of things you can try to avoid this issue:

a) Make the JLabel's text blank, rather than empty, e.g. myLabel.setText(" ") rather than myLabel.setText("");

b) Change the text's color to match the label's background color whenever there isn't supposed to be any text.

You may have some luck with putting the label into a BorderLayout within one cell of the GridBagLayout, in hopes that the "greedy" nature of the BorderLayout expands the component to take as much space as is available, but I haven't tried it myself, so I can't say for sure.