0

I have two labels, one I want to take up 30% of the available horizontal space and another one that takes up 70%. This is absolute so I don't want it to be 30%/70% of what the layout feels it has over, 30%/70% total.

I have foremost tested with GridbagLayout as that is usually the way to go when you want these kinds of specific configurations but I can't figure out a way to get these specific sizes and still have the data to render part of the label that is suppose to take up 30%.

Basic code example without any layout modifications:

        frame = new JFrame();
        frame.setSize(200,200);
        JPanel panel = new JPanel();

        // 30% Label
        panel.add(new JLabel("Very long sentence that should be cut off"));
        // 70% Label
        panel.add(new JLabel("Text"));

        frame.setContentPane(panel);
        frame.setVisible(true);
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
David S
  • 195
  • 5
  • 19
  • 1
    In order to ignore the "default" size of a component, you should override its preferredSize attribute (what the layout manager uses to determine the size of the component). Then you can then easily carry out what you want. See [this page](https://docs.oracle.com/javase/tutorial/uiswing/layout/problems.html) – hexstorm May 20 '21 at 20:16
  • 1
    *"30%/70%"* That's a pretty arbitrary way to assign space to components. I doubt any core layout will be suitable, and since it seems to be part of an 'unusable GUI' in the making, I also doubt people (worth listening to) will be keen to devote thought to it. – Andrew Thompson May 20 '21 at 20:19
  • @AndrewThompson I understand that normally this is frowned upon but for my specific use I think it's valid, I have a list of strings to display with varying sizes some a couple of characters long others over 100 long. When I put all of them into the same panel it causes obvious issues that the super long strings makes the whole panel blob up, would prefer if it was cut-off instead. – David S May 21 '21 at 14:38
  • 1
    Well I sure hope you add the same string as a tool tip. BTW - have you tried using a `JSplitPane` instead? It leaves the choice in the user's hands, and provides the [`JSplitPane.setResizeWeight(double)`](https://docs.oracle.com/en/java/javase/15/docs/api/java.desktop/javax/swing/JSplitPane.html#setResizeWeight(double)) method. – Andrew Thompson May 21 '21 at 15:20
  • @AndrewThompson UI is navigated by a controller so I'm not sure how well that would control. – David S May 21 '21 at 19:09

1 Answers1

3

None of the standard layout managers provide this functionality directly.

Check out the Relative Layout. It will allocate space to each component as a percentage of the total space available. The allocation will change dynamically as the available space changes.

Basic logic:

RelativeLayout rl = new RelativeLayout(RelativeLayout.X_AXIS);
JPanel panel = new JPanel( rl );
panel.add(smallLabel, new Float(3));
panel.add(largeLabel, new Float(7));

Using the standard layout managers you might be able to use the BoxLayout. It respects the "maximum" size of components, so you might be able to override the getMaximumSize() size method to do what you want.

Something like:

import java.awt.*;
import javax.swing.*;

public class SSCCE extends JPanel
{
    public SSCCE()
    {
        setLayout( new BoxLayout(this, BoxLayout.X_AXIS) );

        JLabel small = new JLabel("some long text that will get truncated")
        {
            @Override
            public Dimension getMaximumSize()
            {
                Dimension size = getParent().getSize();
                Dimension maximum = super.getMaximumSize();
                maximum.width = (int)(size.width * .3);

                return maximum;
            }
        };
        small.setOpaque(true);
        small.setBackground(Color.YELLOW);
        add(small);

        JLabel large = new JLabel("text")
        {
            @Override
            public Dimension getMaximumSize()
            {
                Dimension size = getParent().getSize();
                Dimension maximum = super.getMaximumSize();
                maximum.width = (int)(size.width * .7);

                return maximum;
            }
        };
        large.setOpaque(true);
        large.setBackground(Color.ORANGE);
        add(large);
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("SSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new SSCCE());
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

    public static void main(String[] args) throws Exception
    {
        java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
    }
}
camickr
  • 321,443
  • 19
  • 166
  • 288
  • Is there a reason to override the `getMaximumSize()` of the components instead of calling `setMaximumSize()`, or just good practice? – hexstorm May 21 '21 at 06:18
  • 1
    @hexstorm Specifically in this case the maximum is dynamic since it is based on the size of the parent container as the frame is resized. Generally overriding "getter" method is the better approach. For example, what if the text (or any other property) of the label is changed? This could affect the size. Also, the size is a property of the component, so the logic should be part of the component, not some random value determined externally to the component. – camickr May 21 '21 at 13:34
  • @camickr Thanks for the detailed example, super helpful! I will try to test it during the weekend. – David S May 21 '21 at 18:19