Nested JScrollPanes: who gets a scroll bar ?

I have a JFrame that contains a JScrollPane (A) taht contains a JTabbedPane that contains a JScrollPane (B) that contains a JTable. When I resize the window, I get scrollbars. But I can't control where. I would like scroll bars to appear on the root JScrollPane (A) only as a "last resort" when there is no other way to represent the JTabbedane (for example if it has hundreds of tabs). The rest of the time, I would like to have scrollbars only for the JTable (B).

Here is a code example:

package test;

import java.awt.BorderLayout;

import javax.swing.*;

import javax.swing.table.*;

publicclass NestedScrollpanesextends JFrame{

privatestaticfinallong serialVersionUID = 1;

private TableModel getTableModel(){

returnnew DefaultTableModel(new Object[]{

"A","B","C","D","E",

"F","G","H","I","J",

"K","L","M","N","O"}, 100);

}

public NestedScrollpanes(){

super("Test");

JPanel rootPanel =new JPanel(new BorderLayout());

JTabbedPane tabbedPane =new JTabbedPane();

JScrollPane rootScrollPane =new JScrollPane(tabbedPane);// A

JTable table =new JTable(getTableModel());

JScrollPane tableScrollPane =new JScrollPane(table);// B

getContentPane().add(rootPanel, BorderLayout.CENTER);

rootPanel.add(rootScrollPane);

tabbedPane.addTab("TAB", tableScrollPane);

setSize(100, 100);

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

setVisible(true);

}

publicstaticvoid main(String[] args){

new NestedScrollpanes();

}

}

Any idea ?

[3318 byte] By [Roukinea] at [2007-11-26 16:18:51]
# 1

Taking a step back, nested scroll panes are usually inherently problematic and often unintuitive, so you may want to consider a different approach.

If the number of tabs could genuinely be an issue you could use a scrollable tab area (see JTabbedPane.setTabLayoutPolicy()) or a drop-down tab picker (eg the Eclipse style). That's assuming the tabs are user-generated; if they're code-generated I'd suggest that's a symptom of poor UI design.

Essentially, if you can design your UI without needing to address this problem (I'll bet a big pile of cash that you can) then do, because nested scrolling isn't easy to deal with as an end user.

itchyscratchya at 2007-7-8 22:42:09 > top of Java-index,Desktop,Core GUI APIs...
# 2

Hundreds of tabs was just the most obvious example I had. Actually, my real application do not generate so many tabs :) (Though, I agree with the unintuitivelyness of nesting scroll panes. Sometimes it is still the most intuitive solution, such as the TextArea widget in a web page)

I am just stuck in a graphical framework that wraps my form inside such a JScrollPane (because some of our forms are made of many not-resizeable components and we my need to scroll on small screens). I have access to the framework, so I may be able to workaround the problem by disabling the feature. But this sounds like a little dirty, as we may have forms with big tables and lots of not-resizeable components...

I kept searching the doc, but I couldn't find any kind of "priority" feature on JScrollPane.

null

Roukinea at 2007-7-8 22:42:09 > top of Java-index,Desktop,Core GUI APIs...
# 3

How's this:

import java.awt.BorderLayout;

import java.awt.Dimension;

import java.awt.Rectangle;

import javax.swing.*;

import javax.swing.table.*;

public class NestedScrollpanes extends JFrame {

private static final long serialVersionUID = 1;

private TableModel getTableModel() {

return new DefaultTableModel(new Object[] {

"A", "B", "C", "D", "E",

"F", "G", "H", "I", "J",

"K", "L", "M", "N", "O"}, 100);

}

public NestedScrollpanes() {

super("Test");

JPanel rootPanel = new JPanel(new BorderLayout());

JTabbedPane tabbedPane = new OptionalScrollTabbedPane();

JScrollPane rootScrollPane = new JScrollPane(tabbedPane); // A

JTable table = new JTable(getTableModel());

JScrollPane tableScrollPane = new JScrollPane(table); // B

JPanel panel = new JPanel();

panel.setLayout( new BoxLayout( panel, BoxLayout.Y_AXIS ) );

for ( int k = 0 ; k < 50 ; k++ )

panel.add( new JTextField() );

getContentPane().add(rootPanel, BorderLayout.CENTER);

rootPanel.add(rootScrollPane);

tabbedPane.addTab("ScrollPane", tableScrollPane);

tabbedPane.addTab( "TextFields", panel );

setSize(200, 200);

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

setVisible(true);

}

public static void main(String[] args) {

new NestedScrollpanes();

}

public static class OptionalScrollTabbedPane extends JTabbedPane implements Scrollable {

private int unitIncrement = new JTextField().getPreferredSize().height;

public Dimension getPreferredScrollableViewportSize() {

return getPreferredSize();

}

public int getScrollableBlockIncrement( Rectangle visibleRect, int orientation, int direction ) {

int block;

JViewport view = (JViewport) SwingUtilities.getAncestorOfClass( JViewport.class, this );

if ( view != null )

block = orientation == SwingConstants.VERTICAL ? view.getHeight() : view.getWidth();

else // arbitrarily set block size to one-tenth the dimension

block = ( orientation == SwingConstants.VERTICAL ? getHeight() : getWidth() ) / 10;

return block;

}

public boolean getScrollableTracksViewportHeight() {

boolean rv = false;

int index = getSelectedIndex();

if ( index != -1 ) {

rv = ( getComponentAt( index ) instanceof JScrollPane );

}

return rv;

}

public boolean getScrollableTracksViewportWidth() {

boolean rv = false;

int index = getSelectedIndex();

if ( index != -1 ) {

rv = ( getComponentAt( index ) instanceof JScrollPane );

}

return rv;

}

public int getScrollableUnitIncrement( Rectangle visibleRect, int orientation, int direction ) {

return unitIncrement;

}

}

}

JayDSa at 2007-7-8 22:42:09 > top of Java-index,Desktop,Core GUI APIs...
# 4
> How's this:This is stunning.I will have a look at it. Thanks.
Roukinea at 2007-7-8 22:42:09 > top of Java-index,Desktop,Core GUI APIs...
# 5
Many thanks again, JayDS.I managed to integrate your solution to our framework. I will keep it warm in my "cheat sheet" ;-)
Roukinea at 2007-7-8 22:42:09 > top of Java-index,Desktop,Core GUI APIs...