Java / Swing / SpringLayout bug - not for the faint of heart.

Discussion in 'Mac Programming' started by toddburch, Mar 24, 2007.

  1. macrumors 6502a

    Joined:
    Dec 4, 2006
    Location:
    Katy, Texas
    #1
    This is eating my lunch. There is a lot of code here, and the problem lies somewhere with not being able to convince the SpringLayout layout manager that he needs to recheck his constraints.

    First, the file name should be MySLO.java.

    The problem lies with the JTextArea in the middle of the application window. If the window is resized, the layout goes awry.

    When the window is resized to the right:
    • all buttons and the label stay put as desired, and
    • the JTextArea expands to the right as desired.

    So far, so good.

    However, when the window is resized to the left:
    • all buttons and the label will move to the right (evensomuch as off-screen) and
    • the JTextArea is not resized smaller to fix how the constraints are defined.

    All this occurs when JTextArea.setLineWrap(true) is set.

    If setLineWrap(false) is set, the behavior changes:

    When the window is resized left or right, all buttons and the label stay where they are supposed to, and the JTextArea is resized properly.

    However, when you type, you get no line wrapping, and the box is now worthless for long phrases. I guess, this scenario is working as designed. But, I need the line wrap, so it's not really applicable to my needs.

    Here's the code. If you can figure this out, I owe you a beer (or a kool-aid! ;) )

    Todd

    p.s.
    • newbie warning with Java...
    • Code in question is RED
    • I've used adapter classes for the buttons

    Code:
    // My SpringLayOut bug.  
    public class MySLO extends javax.swing.JFrame {
    
    	static javax.swing.JTextArea myText ; 
    
    	// Create a window with all the GUI stuff, and display it. 
    	public static void main(String[] args) { 
    		javax.swing.JFrame win = new MySLO() ; 
    		win.setVisible(true) ;  // make window appear 	
    	}
    	
    
    	// This is the GUI window maker.  It builds the buttons, does the layout, 
    	// registers for events, etc... 
    	public MySLO()  { 
    		super("My SpringLayout Bug - Stretch Window to the Right, then Left, to see the bug") ;   // This is the window title 	
    		
    		// Get the JFrame's container. 
    		java.awt.Container content = this.getContentPane() ; 
    
    		// Set the Layout Manager
    		javax.swing.SpringLayout myLayout = new javax.swing.SpringLayout() ; 
    		content.setLayout(myLayout) ; 
    
    		
    		// Connect and Disconnnect Buttons. 
    		javax.swing.JButton buttonConnect = new javax.swing.JButton("Connect") ; 
    		buttonConnect.addActionListener(new ButtonConnect(this)) ; 
    		content.add(buttonConnect) ; 
    		
    		javax.swing.JButton buttonDisconnect = new javax.swing.JButton("Disconnect") ; 
    		buttonDisconnect.addActionListener(new ButtonDisconnect(this)) ; 
    		content.add(buttonDisconnect) ; 		
    		
    		
    		// This is the label for the box that is screwing up 
    		javax.swing.JLabel enterLabel = new javax.swing.JLabel() ; 
    		enterLabel.setText("Enter Text:") ; 
    		content.add(enterLabel) ; 
    		
    		[color=red][b]// This is the JTextArea box that is screwing up 
    		myText = new javax.swing.JTextArea(10,50) ; 
    		
    		// If this setLineWrap is set to false, the problem is that the TextArea grows to the right 
    		// while the window stays the same size.  Pretty soon, you can't see what you type.  
    		
    		// If this setLineWrap is set to true, there is an issue with resizing the whole window - all 
    		// the other widgets move incorrectly, and this TextArea can grow but will never shrink. 
    		
    		myText.setLineWrap(true) ;       // Cause it to wrap lines.  Change to false for no line wrap.
    		
    		// Add a listener so I can force a validation of the layout - but it has no effect... :( 
    		
    		myText.addComponentListener(new java.awt.event.ComponentListener() { 
    			public void componentResized(java.awt.event.ComponentEvent e) { 
    				System.out.println("Validating...") ; 
    				validate() ;   // In theory, this should validate the layout!!  
    			}
    			public void componentShown(java.awt.event.ComponentEvent e ) { } 
    			public void componentMoved(java.awt.event.ComponentEvent e ) { } 		
    			public void componentHidden(java.awt.event.ComponentEvent e) { } 
    			} ) ; 
    		content.add(myText) ;[/b][/color] 
    
    		javax.swing.JButton buttonClear = new javax.swing.JButton("Clear") ; 		
    		buttonClear.addActionListener(new ButtonClear(this)) ; 
    		content.add(buttonClear) ; 
    
    		javax.swing.JButton buttonSubmit  = new javax.swing.JButton("Submit") ; 		
    		buttonSubmit.addActionListener(new ButtonSubmit(this)) ; 
    		content.add(buttonSubmit); 
    		
    		this.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE) ; // Close app when RED X is clicked.
    
    		// The following code sets up the Spring Layout Constraints - it positions everything 
    		// in the window and influences object placement when the window is resized.
    
    		// Row 1  - Constrain to the Top & Left Edge
    		myLayout.putConstraint(javax.swing.SpringLayout.WEST,  buttonConnect,  135, javax.swing.SpringLayout.WEST, content);	
    		myLayout.putConstraint(javax.swing.SpringLayout.NORTH, buttonConnect,  15, javax.swing.SpringLayout.NORTH, content);
    				
    		myLayout.putConstraint(javax.swing.SpringLayout.WEST,  buttonDisconnect,  15, javax.swing.SpringLayout.EAST, buttonConnect);	
    		myLayout.putConstraint(javax.swing.SpringLayout.NORTH, buttonDisconnect,  15, javax.swing.SpringLayout.NORTH, content);
    		
    		// Row 2  - Constrain to the Top and Left Edge. 
    		myLayout.putConstraint(javax.swing.SpringLayout.EAST,  enterLabel,  120, javax.swing.SpringLayout.WEST,  content);
    		myLayout.putConstraint(javax.swing.SpringLayout.NORTH, enterLabel,   50, javax.swing.SpringLayout.NORTH, content);
    
    		myLayout.putConstraint(javax.swing.SpringLayout.WEST,  myText, 135, javax.swing.SpringLayout.WEST,  content);
    		myLayout.putConstraint(javax.swing.SpringLayout.NORTH, myText,  50, javax.swing.SpringLayout.NORTH, content);
    
    		// Row 3  - Constrain to the Top and Left Edge 
    		myLayout.putConstraint(javax.swing.SpringLayout.WEST,  buttonClear,  135, javax.swing.SpringLayout.WEST,  content);
    		myLayout.putConstraint(javax.swing.SpringLayout.NORTH, buttonClear,  15, javax.swing.SpringLayout.SOUTH, myText);
    
    		myLayout.putConstraint(javax.swing.SpringLayout.WEST,  buttonSubmit,  15, javax.swing.SpringLayout.EAST,  buttonClear);
    		myLayout.putConstraint(javax.swing.SpringLayout.NORTH, buttonSubmit,  15, javax.swing.SpringLayout.SOUTH, myText);
    
    		// Constrain to the Bottom.
    		myLayout.putConstraint(javax.swing.SpringLayout.SOUTH, content,  15, javax.swing.SpringLayout.SOUTH, buttonSubmit);
    		
    		// Constrain to the right Edge 
    		myLayout.putConstraint(javax.swing.SpringLayout.EAST, content,  20, javax.swing.SpringLayout.EAST, myText);			 
    
    		this.pack() ;   // Resize the window as needed.
    		
    		// Center the Dialog on the screen. 
    		java.awt.Dimension screen = java.awt.Toolkit.getDefaultToolkit().getScreenSize() ; 
    		int x = (screen.width  - this.getWidth() ) / 2 ; 
    		int y = (screen.height - this.getHeight() ) / 2 ; 
    		setBounds(x,y,this.getWidth(),this.getHeight()) ; 
    	}
    	
    	// Adapter Classes - to separate the GUI from the application logic (not that there IS any appl. logic yet...
    	
    	class ButtonClear implements java.awt.event.ActionListener { 
    		MySLO data ; 
    		ButtonClear (MySLO data ) { 
    			this.data = data ; 
    		}
    		public void actionPerformed( java.awt.event.ActionEvent ae) { 
    			data.myText.setText("") ; 
    			System.out.println("Input data has been cleared") ; 			
    		}
    	}
    
    	class ButtonSubmit implements java.awt.event.ActionListener { 
    		MySLO data ; 
    		ButtonSubmit (MySLO data ) { 
    			this.data = data ; 
    		}
    		public void actionPerformed( java.awt.event.ActionEvent ae) { 
    			System.out.println("Submit Button") ; 
    		}
    	}
    
    	class ButtonConnect implements java.awt.event.ActionListener { 
    		MySLO data ; 
    		ButtonConnect (MySLO data ) { 
    			this.data = data ; 
    		}
    		public void actionPerformed( java.awt.event.ActionEvent ae) { 
    			System.out.println("Connect Button") ; 
    		}
    	}
    
    	class ButtonDisconnect implements java.awt.event.ActionListener { 
    		MySLO data ; 
    		ButtonDisconnect (MySLO data ) { 
    			this.data = data ; 
    		}
    		public void actionPerformed( java.awt.event.ActionEvent ae) { 
    			System.out.println("Disconnect Button") ; 
    		}
    	}
    }
     
  2. thread starter macrumors 6502a

    Joined:
    Dec 4, 2006
    Location:
    Katy, Texas
    #2
    After countless hours of trying to get this to work, I've decided to can this layout manager and find a more user friendly one. There are too many aspects of this layout manager that are not working as expected, or, it's not documented good enough for me to grasp it.

    Todd
     
  3. macrumors 603

    jeremy.king

    Joined:
    Jul 23, 2002
    Location:
    Fuquay Varina, NC
    #3
    Todd, you may want to consider a BorderLayout based on your GUI so far.

    Also, consider putting your JTextArea inside a JScrollPane so scrollbars can be used.
     
  4. thread starter macrumors 6502a

    Joined:
    Dec 4, 2006
    Location:
    Katy, Texas
    #4
    OK, thanks for the tip. My to-do tonight is to read up on all the layout managers. Will do the scrollbar too. Got to read up on that as well.

    Todd
     
  5. macrumors newbie

    Joined:
    Jul 31, 2006
    Location:
    Huntsville Al
    #5
    Text Area layouts

    I have seen this issue before. The usual fix is to add the text area to either a scroll pane or a JPanel then to the top level componet. Also note that you are adding the resize listener to the text area itself. This will not catch all changes to the top level components.
     
  6. thread starter macrumors 6502a

    Joined:
    Dec 4, 2006
    Location:
    Katy, Texas
    #6
    Yes, I did notice that last night. "Validate...'" would print out once for the initial display, and then again only if I typed so much data that the box itself changed size.

    Perhaps I'll add the JScrollPane to see the effects before leaving for greener pastures.

    I've read about adding top-level component listeners (parent objects) versus adding component-specific listeners, and now as you've pointed out - this point really sinks it in. :)

    Todd
     
  7. macrumors newbie

    Joined:
    Jun 25, 2006
    #7
    Why not try BorderLayout and FlowLayout

    Here is a quick prototype I did in Fabric. Not pretty, but I guess it would work.

    Code:
    <jfc>
    <layout class="java.awt.BorderLayout">
        <layout class="java.awt.BorderLayout" constraint="North">
            <layout class="java.awt.FlowLayout" constraint="West">
                <component class="javax.swing.JButton" text="Connect"/>
                <component class="javax.swing.JButton" text="Disconnect"/>
            </layout>
        </layout>
        <layout class="java.awt.BorderLayout" constraint="Center">
            <component class="javax.swing.JLabel" text="Enter text:" constraint="North"/>
            <component class="javax.swing.JScrollPane" constraint="Center">
                <property name="viewportView" type="Component">
                    <component class="javax.swing.JTextPane"/>
                </property>
            </component>
        </layout>
        <layout class="java.awt.BorderLayout" constraint="South">
            <layout class="java.awt.FlowLayout" constraint="West">
                <component class="javax.swing.JButton" text="Clear"/>
                <component class="javax.swing.JButton" text="Submit"/>
            </layout>
        </layout>
    </layout>
    </jfc>
    
    Which looks like this on Windows and OS X:

    [​IMG]

    [​IMG]
     
  8. thread starter macrumors 6502a

    Joined:
    Dec 4, 2006
    Location:
    Katy, Texas
    #8
    Tepppefalguy, that seems pretty powerful. Looks like I would still need to be well versed in layout managers - you're even combing two different ones there.

    I read the PDF on your website. How would you attach events to this? Would it work with Adapter classes? Is this something you sell?

    Todd
     

Share This Page