JDialog under both Nimbus and Metal look and feel too wide - java

I have two simple JDialog dialogs that should be small. The first has a JTextField of 5 columns and a vertical slider under it. The second has a JLabel, a JSpinner and two JButton(s). Both, after pack() and setVisible(), are too wide. For example, the first dialog has a preferred size of about 80, but shows up with width of 258.
Here are a few things that do not work: 1) a custom layout with the right preferred size computations vs. GridBagLayout; 2) overriding getPreferredSize(); 3) setSize and setBounds before or after setVisible; 4) a custom root pane UI with a custom title pane; 5) a forced constant width set on componentResized; 6) removing the slider (in case there is confusion between vertical and horizontal sliders width and height); 7) removing all controls and the dialog title.
In general, the dialogs can be resized by the user to have smaller width (by dragging the corners), but not programmatically (they can be resized programmatically with setBounds to have smaller height).
The dialogs do have the right sizes under some commercial look and feels, but not under Metal or Nimbus. Under both Metal or Nimbus, the preferred and minimum sizes of the dialog, root panes, glass panes, layered panes make sense. The size of the dialog itself doesn't.
I have tested this without setting a look and feel (which, on Windows, presumably means Metal) and it does not work.
I know that the width of 258 is set in addNotify in Dialog, on getComponentFactory().createDialog(this).
I assumed this could be related to the title portion of the dialog, but the icons there are not of some significant size.
Any ideas are appreciated.
My next move will be to create a simple standalone JDialog or a JFrame that calls a JDialog, outside of the main application with which I am working now. However, I cannot see anything special happening in the larger application.
Here is the code that will produce a dialog with the same width (looks like):
public class Main
{
public static void main(String[] args)
{
JDialog dlg = new JDialog();
dlg.pack();
dlg.setVisible(true);
}
}

Related

How to Remove Black Rectangle on Window Resize?

I am developing a JFrame window with Swing and AWT, and when I resize the window, it looks like this:
Window resize
(I apologize for the low frame rate, stackoverflow wouldn't accept a larger file size)
As you can see, a lot of times when I resize the window, it shows a black rectangle where it is being resized and it doesn't go away until you pause for a moment. Additionally, the circle doesn't always update accurately with my resize event:
frame.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent componentEvent) {
width = frame.getWidth();
height = frame.getHeight();
springLayout.putConstraint(SpringLayout.SOUTH, panel, height, SpringLayout.NORTH, frame.getContentPane());
springLayout.putConstraint(SpringLayout.EAST, panel, width, SpringLayout.WEST, frame.getContentPane());
panel.repaint();
}
});
That's beside the point (though it is possible the two problems are linked). I have only ever encountered this problem when using Swing. JavaFX has never given me this problem. Is there any way I can remove the black rectangle when the window is resized?
I have only tested this on Windows 10.
This apparently has to do with the native window decorations of the window that hosts the JFrame. When disabling the native window decorations, you can remove the stuttering resizes and the black background bleeding through. See the documentation for JFrame.setDefaultLookAndFeelDecorated(boolean):
Provides a hint as to whether or not newly created JFrames should have their Window decorations (such as borders, widgets to close the window, title...) provided by the current look and feel. If defaultLookAndFeelDecorated is true, the current LookAndFeel supports providing window decorations, and the current window manager supports undecorated windows, then newly created JFrames will have their Window decorations provided by the current LookAndFeel. Otherwise, newly created JFrames will have their Window decorations provided by the current window manager.
Thus, you have two options. Either set the property once before you create your JFrame:
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame();
Or do it after the creation of the JFrame:
JFrame frame = new JFrame();
frame.setUndecorated(true);
frame.setWindowDecorationStyle(JRootPane.FRAME);
Note that in either case, you are bound to use Swing's Look and Feel for the window decorations. The title bar and handles of the window are therefore going to look different than before.

How to resize jdialog buttons?

Here's the code:
JOptionPane pane = new JOptionPane(findArray, JOptionPane.QUESTION_MESSAGE, JOptionPane.DEFAULT_OPTION);
pane.setOptions(new Object[]{findPreviousButton, findNextButton});
final JDialog dialog = pane.createDialog(myJFrame, "Find");
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setVisible(true);
findArray consists of JLabel findLabel and JTextField findField. myJFrame is the JFrame. findPreviousButton and findNextButton are the two JButtons I am replacing the default "OK" and "Cancel" buttons with. The both have custom icons and no text. The JDialog window is making their icons a certain size making them look pixelated. How do I resize the buttons so that the width is 60 and the height is 30? The method .setSize(int, int) doesn't work and neither does .setBounds(int, int, int, int)
Adjusting the button margins should help:
http://docs.oracle.com/javase/7/docs/api/javax/swing/AbstractButton.html#setMargin(java.awt.Insets)
You'll probably want to use .setPreferredSize() instead of .setSize(). Usually when I want to override a component's natural size (meaning how it's laid out by whatever layout manager I'm using), I'll use that along with setting the minimum size as well. If a layout manager is modifying the component's natural size, the preferredSize() might be a better option. There's a good stackoverflow discussion about the differences here:
Java: Difference between the setPreferredSize() and setSize() methods in components

Calculate sizes of Components of JDialog with total Size set before making visible

I have a JDialog and I want to have it a certain, given size:
JDialog dialog = new JDialog();
dialog.setSize(800, 600);
dialog.setResizable(false);
Then I add a component:
JLabel label = new JLabel("Test");
dialog.add(label);
Now I could make the dialog visible and check the size of the component
dialog.setVisible(true);
System.out.println(label.getSize());
The answer would be "[width=784,height=562]". Obviously the component was resized to fill the whole client area / content pane of the dialog window. That's fine and as I want it.
Question: How can I obtain the final size of the components before calling setVisible(true)?
getPreferredSize() will not be the size I want because I want the component to adapt to the given dialog's size
dialog.pack() is also not the right thing because it resizes the dialog to the preferred size of the components which I don't want
dialog.validate() does nothing useful here, the size of the component is still 0
dialog.getLayout().layoutContainer(dialog) also does not set the size of the components
So I am at a loss here. I want to make the layoutmanager calculating the right sizes of all components and sub components before showing the dialog, adapted to the overall size of the dialog. But I don't know how.
I now found that it can be done as follows:
JDialog dialog = new JDialog();
JLabel label = new JLabel("Test");
dialog.add(label);
// pack(), setSize(), validate() in this order will
// set sizes on all components as wished
dialog.pack();
dialog.setSize(800, 600);
dialog.validate();
System.out.println(label.getSize());
Also here the output is "[width=784,height=562]" but the dialog is not yet visible. The important part is the combination of pack(), setSize(desiredSize) and validate() in this order. The pack() probably determines a new size of the dialog (preferred sizes of all components), that's why here the size has to be set afterwards and the validate() is responsible for the resizing of the components. Probably setVisible(true) which arrives at the same sizes is doing internally something similar.
It seems a bit of a waste to resize the components several times but without pack() also setSize() and validate() do not have any effect.
I guess the other answers were based on some misunderstanding because they implicitly always assumed that you want to have the preferred size, but there are cases, e.g. if the user resizes the dialog or if the dialogs size is fixed from the beginning where you cannot attain the preferred size and some components just have to fill the available space.
That was the layout problem here, having a given global size of the dialog and determining the size of the components as they fill the available space. LayoutManagers solve this problem quite nicely, however usually only after setVisible(true).
I tested a bit more:
// new dialog
JDialog dialog = new JDialog();
// new label, prints messages if resized or painted
JLabel label = new JLabel("Test") {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Component painted.");
}
};
label.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
System.out.println("Resized: " + e.getComponent().getSize());
}
});
dialog.add(label);
System.out.println("Size after new JLabel: " + label.getSize());
// pack dialog - necessary for setSize/validate to work
dialog.pack();
System.out.println("Size after pack: " + label.getSize());
// set a size and validate changes sizes
dialog.setSize(800, 600);
dialog.validate();
System.out.println("Size after setSize and validate: " + label.getSize());
// set visible would have also done the trick
dialog.setVisible(true);
System.out.println("Size after setVisible(true): " + label.getSize());
// and another resizing (no validation neccessary)
dialog.setSize(300, 200);
// dispose
dialog.dispose();
And the output is
Size after new JLabel:java.awt.Dimension[width=0,height=0]
Size after pack: java.awt.Dimension[width=116,height=16]
Size after setSize and validate: java.awt.Dimension[width=784,height=562]
Size after setVisible(true): java.awt.Dimension[width=784,height=562]
Resized: java.awt.Dimension[width=284,height=162]
Resized: java.awt.Dimension[width=284,height=162]
Component painted.
I learned more about the inner workings of Swing:
ComponentResized events are not fired before setVisible(true) even if components are resized (their size changes)
ComponentResized events even with the same size can be fired several times in a row
Components might not be painted in between resizing if they follow each other fast enough
The first painting is in any case after setVisible(true) and the component will have the desired size (preferred size or defined by other constraints as here) by then.
If for some reason you must know the size of the components before the first drawing, do it with pack(), setSize(), validate()
I tested some more, also with maximized frames and now can combine all the results into: The first painComponent() is always with the right size and the related componentResized() event always follows afterwards, sometimes two times.However the LayoutManager must know before, otherwise the examples would not be drawn correctly. So in case one draws the background by itself, either read out the right size in every paintComponent or implement a custom layout manager or wait for the resized event and invoke repaint, so the component is drawn two times but it should work. Apllications include cases where the number of components to show depend on the size (as in my geographical map application).
Just to complete the picture I think the flow goes like this in case a user maximized or resized a frame/dialog:
frame/dialog.setSize()
LayoutManager.layoutContainer(frame/dialog) using the actual size
frame/dialog paint() using the layouted sizes
Resized() events fired for all components etc.
And pack() probably just calls setSize(layout.preferredLayoutSize()) as the first step.
So in case depending on the size you have to add or remove components for example, it could be a good idea to override setSize() and listen there for changes. I initially was listening for Resized() events but they arrive too late for the first drawing.
Top-Level Containers return own Size, PreferredSize, Bounds in two cases (if they are)
already visible
after call pack(), in your case dialog.pack();
have to calculating with Borders and ToolBar came from Native OS
have to get this size from Top-Level Containers#getContentPane().getWhatever
most of JComponents returns own PreferredSize, then there no reason to sizing for Standard Layout Manager

What are side effects of setPreferredSize?

I have window containing multiple panels. I don't have access to window code. (I can modify only panel’s code.)
I removed few components from panel. Window has shrunk its size. But window is too small to display everything correctly.
I added line setPreferredSize(getPreferredSize());. Now window have right size.
What are side effects of setPreferredSize?
Edit: BorderLayout is used. Which should ignore getXXXSize(). My panel is in CENTRE. Panel which doesn't fit the screen is on NORTH.
This is what is happening:
getPreferredSize() looks whether the size was set before. If not, the method asks the LayoutManager of the component itself (which is your JPanel) about the preferred size, which is then calculated from the components.
setPreferredSize(...) then sets this value on the JPanel, memorizing it for later.
Later you remove some components of the JPanel.
Even later, when the window tries to re-layout itself (or is told to do so), the window's (or contentpane's/RootPane's/...) Layoutmanager calls your JPanel's getPreferredSize() method again.
now getPreferredSize() does not ask the JPanel's LayoutManager, but simply returns the stored size previously set by setPreferredSize().
For width, the BorderLayout is ignoring the preferred width of the NORTH and south component, it only takes CENTER, EAST and WEST into account. (Similarly for height).
I just took a look at the implementation of BorderLayout.preferredLayoutSize (in 1.6.0_13 from Sun), and it works like this:
The width is calculated as
max( EAST.width + CENTER.width + WEST.width + h-gaps,
NORTH.width, SOUTH.width ) + insets
The height is calculated as
max( EAST.height, CENTER.height, WEST.height)
+ NORTH.height + SOUTH.height + v-gaps + insets
(Each of the width/height are the values of the preferredSize of these components.)
If some of the five components are missing, their height/width is not included, neither are the gaps.)
It works the same for minimalLayoutSize, while maximumLayoutSize simply returns Integer.MAX_VALUE.
So, in principle it should work out of the box.
But in general, if the layout of the window is not under your control, you should not have to worry about components not under your control being cut off :-)
Yes, it has a lot of effect. The effect depends on
the layout manager in use
component on which the preferred size is set.
The main reason is that once you call setPreferredSize() to set the size, the component will no more ask ui to get the prefered size. Ideally, only the ui delegate of your component knows the ideal pref size of the component. Once you set the pref size yourself, the ui will not be queried for pref size.
For clarity see the Jcomonent.getPreferredSize() code:
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
Dimension size = null;
if (ui != null) {
size = ui.getPreferredSize(this);
}
Now why does it depend on layout-manager, because only some layouts call(use) getPreferredSize() for computation. For e.g flowlayout uses pref size but border layout doesnt
Method setPreferredSize() is side effect free.
How getPreferredSize() works was described by Suraj Chandran.
Why setPreferredSize(getPreferredSize()) worked is explained by Paŭlo Ebermann.

Which Java text component should I be using to get it to resize to fit the frame as it is resized?

I have a JTabbedPane and a JTextArea, I want the Textareas/Panes to resize to fit the window when the window is resized. How do I do this? I'm also fuzzy on if it's the right text component to use.
Really Muddled.
EDIT: I was setting explicit size as a dumb dumb. I've got it all worked out. What I did is set the viewport of a JScrollPane as the JTextArea and remove all explicit sizing.
Thanks chaps!
You can use whatever components you like. All JComponents can resize with a resizing parent container. You simply need to choose the correct LayoutManager.
Depending on how you want everything set up, I assume you will want to add your JTabbedPane to your JFrame like so:
JTabbedPane jtp;
JFrame frame;
// JFrame's default layour is border layout.
frame.add(jtp, BorderLayout.CENTER);
This will cause the JTabbed pane to take up all of the space of the JFrame.
You will also need to set your JTabbedPane's layout manager to BorderLayout, and also any tab you add to the JTabbedPane will need to have a BorderLayout.
To set the layout of your components, do:
JComponent anyComponent;
anyComponent.setLayout(new BorderLayout());
Have a look at the Java LayoutManager tutorials for more information.
I should mention that the reason I suggest BorderLayout is because any component added to the CENTER of a BorderLayout will automatically take up any space not taken up by any of the borders. And, since you aren't adding anything to any of the other borders (NORTH, SOUTH, EAST, WEST) the component in the center will take up the entire space.
Every JComponent is resized automatically when the container it resides in, is resized.
It's not a matter of the right component but more a matter of which LayoutManager to use.
When adding a Component to a container, you can set the minimum size, default size and maximum size.
On resizing the container, the component will scale automatically until reaching the desired maximum size.
If you need code snippets, tell me and I will fire up my IDE later.
cheers
EDIT: jjnguy beat me to it.. nvm then :p

Categories

Resources