The knob on vertical JSlider's on my Windows 7 machine (with native look-and-feel) is really, really tiny in both directions. Not just skinny but short as well.
Can anyone confirm this? Should I report it? If so, where? Thanks!
Here is the code for the sample program (in the screen shot):
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
public class SliderTest
{
public static void main( String[] args )
{
// Set the look and feel to that of the system
try
{ UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() ); }
catch ( Exception e )
{ System.err.println( e ); }
// Launch the GUI from the event dispatch thread
javax.swing.SwingUtilities.invokeLater( new Runnable()
{
public void run ()
{
JFrame window = new JFrame();
window.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
JPanel contentPane = new JPanel();
contentPane.add( new JSlider(SwingConstants.HORIZONTAL) );
contentPane.add( new JSlider(SwingConstants.VERTICAL) );
window.setContentPane( contentPane );
window.pack();
window.setLocationRelativeTo( null ); // Center window
window.setVisible( true );
}
});
}
}
First off, this happens in Windows Vista too. It seems to be the case, that the slider tries to take as little space as possible. If you want a bigger JSlider use JSlider.setPaintTicks. So you have to add the following:
JSlider vertical = new JSlider( SwingConstants.VERTICAL );
vertical.setPaintTicks( true );
contentPane.add( vertical );
That should do the trick.
Related
I am trying to color the Border of a JTextField red and then change it back to "normal" later on. When I am using Linux (furthermore Ubuntu), the initial Border differs from the Border that you get by using UIManager.getBorder("TextField.border"); one of them is a SynthBorder and one is a FieldBorder. The "correct" one would be the SynthBorder.
SSCCE:
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main
{
private static boolean switched;
public static void main( final String[] args )
throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException
{
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
JFrame frame = new JFrame( "Test border change" );
frame.getContentPane().setLayout( new BoxLayout( frame.getContentPane(), BoxLayout.LINE_AXIS ) );
JTextField tf = new JTextField();
JButton button = new JButton( "Switch" );
button.addActionListener( action ->
{
if ( switched )
{
tf.setBorder( UIManager.getBorder( "TextField.border" ) );
switched = !switched;
}
else
{
tf.setBorder( BorderFactory.createLineBorder( Color.RED ) );
switched = !switched;
}
} );
frame.getContentPane().add( tf );
frame.getContentPane().add( button );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.pack();
frame.setVisible( true );
}
}
I have already tried:
using JComponent.updateUI() (no effect)
nulling the border (ruins the layout)
preserving it (not a proper way)
Does anyone have a better idea?
You can get the default border in the UIManager with this code:
jTextField2.setBorder(UIManager.getLookAndFeel().getDefaults().getBorder("TextField.border"));
When you replace the Border try using:
Border uiBorder = BorderUIResource( BorderFactory.createLineBorder( Color.RED ) );
tf.setBorder( uiBorder );
When you use any wrapper class with "UIResource" this tell the LAF the component is part of the LAF and not a custom implementation
Then to restore the Border:
SwingUtilities.updateComponentTreeUI( tf );
Hopefully this will fake the UI into resetting the LAF properties, specifically the Border.
Read the section from the Swing tutorial on How to Set the LAF for more information.
Of course this is not as efficient as simply saving the Border and resetting it as all properties of the text field will be updated bye the updatComponentTreeUI(...) (if this works).
Still don't see why you can't save the Border. You could use the putClientProperty(...) method of the JComponent class to save the Border and then restore it using the getClientProperty(...) method.
You could even automate this by using adding a PropertyChangeListener to listen for a change in the border. When an event is generated if the getClientProperty(...) returns null, then you save the old value from the PropertyChangeEvent.
yourJTextField.setBorder(new JTextField().getBorder());
You can get the border right after creating the component to save it, and set it again later.
Border defaultBorder = tf.getBorder();
...
tf.setBorder(defaultBorder);
I'm trying to keep a JDialog centered inside of a JFrame, regardless if the JFrame is moved or resized.
Essentially, I have a class like (This is not the real code, just an example )
import javax.swing.*;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.Dimension;
import java.awt.Color;
public class HelloWorldSwing extends ComponentAdapter implements AncestorListener {
private JDialog dialog;
private JFrame frame;
#Override
public void componentResized( ComponentEvent event ) {
//This value does not hcange when dragged from the left or right
System.err.println( " Location " + frame.getLocationOnScreen() );
recenter();
}
#Override
public void ancestorAdded( AncestorEvent evt ) {
}
#Override
public void ancestorRemoved( AncestorEvent evt ) {
}
#Override
public void ancestorMoved( AncestorEvent evt ) {
//This value does not hcange when dragged from the left or right
System.err.println( " Location " + frame.getLocationOnScreen() );
recenter();
}
private void recenter() {
dialog.setLocationRelativeTo( frame.getRootPane() );
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private void createAndShowGUI() {
//Create and set up the window.
frame = new JFrame("HelloWorldSwing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getRootPane().addAncestorListener( this );
frame.getRootPane().setBackground( Color.WHITE );
frame.addComponentListener( this );
frame.setMinimumSize( new Dimension( 500, 500 ) );
frame.setMaximumSize( new Dimension( 500, 500 ) );
dialog = new JDialog();
dialog.setResizable( true );
dialog.setAlwaysOnTop( true );
dialog.setVisible( true );
dialog.setMaximumSize( new Dimension( 200, 200 ) );
dialog.setMinimumSize( new Dimension( 200, 200 ) );
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
HelloWorldSwing swing = new HelloWorldSwing();
swing.createAndShowGUI();
}
});
}
}
When the JFrame is resized from the left or the top, the location on the screen changes, but the getLocationOnScreen() does not change it's result. It seems like the getLocationOnScreen should change as the top left corner has changed due to a resize.
What am I missing ? Thanks.
Update ::
This code seems to run fine on Windows, but does not run correctly on Fedora 21, java 7
I have found that when a modal JDialog launches a second modal JDialog, and when that second JDialog is disposed, the first JDialog seems to (mostly) ignore the keyboard.
I have included a sample program to demonstrate the issue. On running the code, I should be able to hit the space bar (the button in the main JFrame has focus) and the first JDialog is launched, containing a button (also with focus).
I hit the space bar and the second JDialog is launched. Hitting enter, the second JDialog disposes.
Hitting space bar on the first JDialog results in no response. Alt- F4 will close the first JDialog, so the keyboard is not completely disconnected.
Repeating the above procedure using the mouse only, the issue does not occur.
The background to all of this is I wanted to set the default button on a dialog (in which the user enters some text and other data). If I comment out the setDefaultButton in the second JDialog and now use TAB to move around, the issue does not occur.
In short, I want to be able to set a default button on a JDialog (so I can hit the enter key), but at the same time, I want default focus on a JTextField.
I am on Ubuntu 13.10 using the OpenJDK (64 bit). I have found this happens regardless of using the GTK+ look and feel or the metal look and feel (that is, not setting any look and feel).
I cannot reproduce on Windows XP (using Oracle Java 32 bit).
On reading a few posts, it seems Linux and Java have a few focus problems and so I'm not sure if this is a programming error on my part or I've stumbled upon a Linux/Java issue.
Any ideas please?
Below is example code which reproduces the issue.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
public class Test extends JFrame
{
public Test()
{
JButton button = new JButton( "Test" );
button.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent actionEvent ) { new Test1( Test.this ); } } );
JPanel panel = new JPanel();
panel.add( button );
getContentPane().add( panel );
pack();
setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE );
setVisible( true );
}
protected class Test1 extends JDialog
{
public Test1( JFrame owner )
{
super( owner );
JButton button = new JButton( "1" );
button.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent actionEvent ) { new Test2( Test1.this ); } } );
JPanel panel = new JPanel();
panel.add( button );
getContentPane().add( panel );
pack();
setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE );
setLocationRelativeTo( null );
setModalityType( ModalityType.APPLICATION_MODAL );
setVisible( true );
}
}
protected class Test2 extends JDialog
{
public Test2( JDialog owner )
{
super( owner );
JTextField textField = new JTextField();
JButton button = new JButton( "2" );
getRootPane().setDefaultButton( button );
button.addActionListener
(
new ActionListener()
{
public void actionPerformed( ActionEvent actionEvent )
{
// Test2.this.getRootPane().setDefaultButton( null ); // Makes no difference!
Test2.this.dispose();
}
}
);
JPanel panel = new JPanel( new BorderLayout() );
panel.add( textField, BorderLayout.CENTER );
panel.add( button, BorderLayout.SOUTH );
getContentPane().add( panel );
pack();
setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE );
setLocationRelativeTo( null );
setModalityType( ModalityType.APPLICATION_MODAL );
setVisible( true );
}
}
public static void main( String[] args )
{
new Test();
}
}
I have a simple JSlider with an attached ChangeListerner. Here's the code:
JSlider slider = new JSlider();
slider.setMinorTickSpacing(2);
slider.setMajorTickSpacing(20);
slider.setPaintLabels(true);
slider.setPaintTicks(true);
slider.setSnapToTicks(true);
slider.setOrientation(SwingConstants.VERTICAL);
contentPane.add(slider, BorderLayout.CENTER);
slider.addChangeListener(new SliderListener());
class SliderListener implements ChangeListener {
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider)e.getSource();
if (!source.getValueIsAdjusting()) {
System.out.println("boo");
}
}
}
As you can see, the code isn't doing much, all I want to do for now is make sure the event is only firing once, and hence my event is simply to print something to the Console within Eclipse.
But the above code is printing "boo" twice each time I change the Slider. I'm guessing this has got something to do with Mouse Release on Slider, but whatever it is, I want it to only fire the event once, and hence only print the word once.
How can I achieve that?
Thanks
Are you certain the listener is not added twice ? The following SSCCE works as expected on my machine (OS X, JDK7)
import javax.swing.JFrame;
import javax.swing.JSlider;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.EventQueue;
public class SliderTest {
public static void main( String[] args ) {
EventQueue.invokeLater( new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame( );
final JSlider slider = new JSlider( 0, 100 );
frame.add( slider );
slider.addChangeListener( new ChangeListener() {
#Override
public void stateChanged( ChangeEvent e ) {
if ( !( slider.getValueIsAdjusting() ) ){
System.out.println( "SliderTest.stateChanged" );
}
}
} );
frame.pack();
frame.setVisible( true );
frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE );
}
} );
}
}
I know the issue. My workaround is to set global eventOnwer to all other listeners,
That solves other event fires to occure while one is the event owner.
And then, to solve the slider, I set the getValueIsAdjusting() to true in a if. In case of another fire, return.
Note: You may have to compile and run my example to fully understand my question. If this is not kosher, I apologize in advance.
I am trying to create a Swing control that is based on a JToggleButton and a JPopupMenu.
The toggle button is selected iff the popup menu is visible, and the toggle button is deselected iff the popup menu is not visible. Thus, the behavior is similar to a JComboBox, except that the popup can contain arbitrary components.
The code that follows is an example of how I would create the control (except that it would be in its own class... something like a JPopupToggleButton). Unfortunately, it exhibits different behavior under different look and feels (I have tested it with Metal and Nimbus).
The code as posted here behaves as expected in Metal, but not in Nimbus. When using Nimbus, just show and hide the popup by repeatedly clicking the toggle button and you will see what I mean.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
public class PopupButtonExample extends JFrame
{
public static void main( String[] args )
{
java.awt.EventQueue.invokeLater( new Runnable()
{
#Override
public void run()
{
PopupButtonExample example = new PopupButtonExample();
example.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
example.setVisible( true );
}
});
}
public PopupButtonExample()
{
super( "Components in Popup" );
JPanel popupPanel = new JPanel();
popupPanel.setLayout( new BorderLayout() );
popupPanel.add( new JLabel( "This popup has components" ),
BorderLayout.NORTH );
popupPanel.add( new JTextArea( "Some text", 15, 20 ),
BorderLayout.CENTER );
popupPanel.add( new JSlider(), BorderLayout.SOUTH );
final JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add( popupPanel );
final JToggleButton popupButton = new JToggleButton( "Show Popup" );
popupButton.addActionListener( new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
if( popupButton.isSelected() )
popupMenu.show( popupButton, 0, popupButton.getHeight() );
}
});
popupMenu.addPopupMenuListener( new PopupMenuListener()
{
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent pme) {}
#Override
public void popupMenuCanceled(PopupMenuEvent pme) {}
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent pme) {
Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
Point componentLoc = popupButton.getLocationOnScreen();
mouseLoc.x -= componentLoc.x;
mouseLoc.y -= componentLoc.y;
if( !popupButton.contains( mouseLoc ) )
popupButton.setSelected( false );
}
});
JPanel toolBarPanel = new JPanel();
toolBarPanel.add( popupButton );
JToolBar toolBar = new JToolBar();
toolBar.add( toolBarPanel );
setLayout( new BorderLayout() );
add( toolBar, BorderLayout.PAGE_START );
setPreferredSize( new Dimension( 640, 480 ) );
pack();
}
}
Commeting out the following lines makes the code behave as expected in Nimbus, but not in Metal. Again, just keep clicking the toggle button to see what I mean.
// Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
// Point componentLoc = popupButton.getLocationOnScreen();
// mouseLoc.x -= componentLoc.x;
// mouseLoc.y -= componentLoc.y;
// if( !popupButton.contains( mouseLoc ) )
So here are my two questions:
(1) In Nimbus, why does the click that hides the popup panel not get passed to the toggle button, as it does with Metal?
(2) How can I solve this problem so that it works with all look and feels?
Nimbus is too buggy (and development ended somewhere in the middle) I see that you need three mouse click to the JToggleButton in compare with Metal
every standard L&F have got own specific issues, especially SystemLookAndFeel
use JWindow rather that JPopup, because with JPopup there are another Bugs too e.g. JPopup with JCombobox
After some investigation, I found the cause for the difference between Nimbus and Metal. The following flag is used (at least by BasicPopupMenuUI) to control the consumption of events when a popup is closed:
UIManager.getBoolean( "PopupMenu.consumeEventOnClose" );
When using Nimbus, this returns true. When using Metal, this returns false. Thus, the method popupMenuWillBecomeInvisible should be defined as follows:
if( UIManager.getBoolean( "PopupMenu.consumeEventOnClose" ) )
{
popupButton.setSelected( false );
}
else
{
Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
Point componentLoc = popupButton.getLocationOnScreen();
mouseLoc.x -= componentLoc.x;
mouseLoc.y -= componentLoc.y;
if( !popupButton.contains( mouseLoc ) )
{
popupButton.setSelected( false );
}
}