3

When I add Swing component (like a JButton) to a JPanel, it renders with it's 'preferred size'.

However, the preferred size is actually larger than the painted button. There appears to be an invisible border around it.

Here's a simple frame with my test panel:

JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

TestPanel pnl = new TestPanel();
frame.getContentPane().add(pnl);

frame.pack();
frame.setVisible(true);

Here's my test panel ...

public class TestPanel extends JPanel {

    JButton btn1 = new JButton("Test1");
    JButton btn2 = new JButton("Test2");

    public TestPanel() {
        this.add(btn1);
        this.add(btn2);
    }

    public void paint(Graphics g) {
        super.paint(g);

        g.setColor(Color.RED);
        Dimension dim = btn1.getPreferredSize();
        g.drawRect(btn1.getX(), btn1.getY(), (int)(dim.getWidth()), (int)(dim.getHeight()));
    }

}

Notice I painted btn1's "PreferredSize" in RED to demonstrate that the preferredSize is actually larger than the button itself.

enter image description here

My question is, how can I determine the width and height of the painted button, not the JButton's preferredSize?

Any help is greatly appreciated, thanks!

UPDATE

Because I actually need this to work for all Swing components, here's a screen shot with the more components.

Unfortunately, I need to figure this out, determining the "real" size of the visible widget is crucial to my application.

enter image description here

mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • I believe (I'm not sure tho) that's part of the implementation of the `JButton` and so is "hidden" (you can check how that extra border is calculated reading the code probably). Anyway, what do you need that value for? There may be another way to achieve the same thing. – asermax Mar 19 '13 at 00:40
  • @asermax An example of what I need to do is dynamically position the JButton (or any Swing widget/JComponent) exactly 10 pixels from the top or bottom of left or right of the window. Currently the JButton is 10 pixels + the invisible border from the edges. There's got to be a way to programmatically determine the width/height of that invisible portion, no? –  Mar 19 '13 at 00:44
  • The only thing I can thing of is using [`setBorderPainted`](http://docs.oracle.com/javase/7/docs/api/javax/swing/AbstractButton.html#setBorderPainted(boolean)) to prevent the border of being painted, in the case that invisible border is actually a border and not painted by the component itself. If my previous assumption is right, then maybe `JComponent`'s [`getBorder`](http://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html#getBorder()) could give you the border and you can use it to obtain it's thickness. – asermax Mar 19 '13 at 00:52
  • @asermax Thanks for the suggestions. Unfortunately btn1.setBorderPainted(false) just causes the button itself to not get painted, but doesn't impact the PreferredSize. I'll look at getBorder and see if there's some kind of CSS-ish margin or padding defined in there. –  Mar 19 '13 at 00:56
  • If an invisible Border is being used then the component must be using a CompoundBorder. You should be able to determine the size of the outer Border being used. – camickr Mar 19 '13 at 00:57
  • @camickr Thanks for the pointer, I'm having so luck with `btn1.getBorder().getBorderInsets(btn1)`, I'll keep you posted. :) –  Mar 19 '13 at 01:06
  • *"I need to figure this out, determining the "real" size of the visible widget is crucial to my application."* What feature does it provide to the application? – Andrew Thompson Mar 19 '13 at 03:02
  • @AndrewThompson Sorry to be secretive, but it's a client's project so I can't say too much. It's a sort of custom layout manager and widgets need to dynamically placed tight along the edges of the panel. This little bit of extra invisible padding is messing it up. –  Mar 19 '13 at 03:42
  • don't think it's messing up - after all the ui used to be done by apple and they seemed to have been happy with the visuals ;-) Try to find the mac style guides which certainly have something to say about proper alignment. Oh, and before jumping into yet another custom super-duper LayoutManager: be sure you read the great examples like Mig, Form, DesignGridBag - they might give some insight into the problem – kleopatra Mar 19 '13 at 10:26

2 Answers2

3

I don't think this is particular or practically achievable.

The problem is, the button is using the "unpainted" area to paint other elements, like the focus highlight.

You could try look at the AbstractButton#set/getMargin

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thanks for the suggestion, but btn1.getMargin() appears to be `[top=0,left=2,bottom=0,right=2]` -- that doesn't account for all the extra space. :( –  Mar 19 '13 at 01:12
  • I turns out, at the end of the day, the 'unpainted' area is consisted to be part of the component on Mac OS X. On Windows, I see the expected behavior. I guess I'm at the mercy of whoever implemented the widgets on the target platform. Tahnks for the help. –  Apr 22 '13 at 20:07
  • That's what I thought. I've had similar issues with the textfield and component borders :P – MadProgrammer Apr 22 '13 at 21:53
3

If nothing better comes along, note that the authors "recommend that you put the component in a JPanel and set the border on the JPanel."

Addendum: Based on your comments below, it's clear that your question is not about rendering borders but about establishing a component's boundary. What you perceive as unused space is actually reserved by the UI delegate for any number of uses, e.g. selection highlighting or esthetic coherence. You can get an idea of how this varies by selecting different Look & Feel themes in the examples here and here.

Using getbounds():

image

Using setBorder():

image

import component.Laf;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;

/**
 * @see https://stackoverflow.com/a/15490187/230513
 */
public class Test {

    private void display() {
        JFrame f = new JFrame("Test");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLayout(new FlowLayout());
        // https://stackoverflow.com/a/11949899/230513
        f.add(Laf.createToolBar(f));
        f.add(decorate(new JButton("Test")));
        f.add(decorate(new JTextField("Test")));
        f.add(decorate(new JTextArea(3, 8)));
        f.add(decorate(new JCheckBox("Test")));
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private JPanel decorate(final JComponent c) {
        JPanel p = new JPanel() {
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Rectangle r = c.getBounds();
                g.setColor(Color.red);
                // NB pen hangs down and to the right
                g.drawRect(r.x - 1, r.y - 1, r.width + 1, r.height + 1);
            }
        };
        p.add(c);
        return p;
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Test().display();
            }
        });
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Thanks, but I only drew the red line on my TestPanel to demonstrate the extent of the widget's "preferredSize". What I'm trying to determine is how can I get the size of the painted area of the button (or other widget). I thought it would be getPreferredSize, but that's actually bigger. –  Mar 19 '13 at 01:58
  • It may help to edit your question in order to clarify the goal; a different marking that doesn't interfere with L&F decorations may be possible. – trashgod Mar 19 '13 at 02:01
  • Thanks for the assistance, but I think the question reads pretty clearly: `"My question is, how can I determine the x, y, width, and height of the painted button, not the JButton's preferredSize?"` If you have any more suggestions, I'm all ears! Thanks. –  Mar 19 '13 at 02:03
  • The UI delegate for each component owns the view; you'll have to decide if it's worth doing a custom implementation. – trashgod Mar 19 '13 at 02:17
  • I don't understand where you're coming from. Basically, I add existing Swing widgets (like JButton) to a panel, and I need to programmatically determine the size of the Swing widget. Example: button.getPreferredSize() returns the size illustrated in my screen shot as a red line, but the button is actually smaller than that. My question is, how do I get the "real" size of the button. Thanks. –  Mar 19 '13 at 02:23
  • 1
    @RobertHume not helping in your problem because it's the same in this context, just: if you want a component's actual size use comp.getSize() The prefSize is a sizing _hint_ for the LayoutManager which it is free to ignore. – kleopatra Mar 19 '13 at 10:19
  • 1
    seems like the huge difference in perceived vs. real bounds is somehow mac specific (in win it's about the same) - so you should read up on mac style guides to find out how that's supposed to be handled (handling meaning proper alignment) – kleopatra Mar 19 '13 at 10:22
  • +1 to @kleopatra on both counts; `AquaLookAndFeel` is a good test target against [misuse of preferred size](http://stackoverflow.com/q/7229226/230513). – trashgod Mar 19 '13 at 10:33
  • @kleopatra Thanks for the help, I'll switch to getSize. –  Mar 19 '13 at 15:47