title: JScrollpane resizing with variable-sized content
body: <p>My resizable JScrollPane's content has a minimum width. If the JScrollPane is smaller than this width, horizontal scroll bars should appear. If it's greater than this width, the viewport content should expand to fill up the entire viewport.</p>
  
  <p>Seems like a simple concept, and I've got something that's working, but it feels like a hack:</p>
  
  <pre><code>import javax.swing.*;
  import java.awt.*;
  import java.awt.event.ComponentAdapter;
  import java.awt.event.ComponentEvent;
  
  public class SSBTest {
      public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                  final Component view = new MyView();
                  final JScrollPane jScrollPane = new JScrollPane(view);
                  jScrollPane.addComponentListener(new ComponentAdapter() {
                      @Override
                      public void componentResized(final ComponentEvent e) {
                          final Dimension minimumSize = view.getMinimumSize();
                          final int width = Math.max(minimumSize.width, jScrollPane.getViewport().getWidth());
                          view.setPreferredSize(new Dimension(width, minimumSize.height));
                      }
                  });
                  showInDialog(jScrollPane);
              }
          });
      }
  
      private static void showInDialog(final JScrollPane jScrollPane) {
          final JDialog dialog = new JOptionPane(jScrollPane).createDialog("JScrollPane Resize Test");
          dialog.setResizable(true);
          dialog.setModal(true);
          dialog.setVisible(true);
          System.exit(0);
      }
  
      private static final class MyView extends JPanel {
          @Override
          protected void paintComponent(final Graphics g) {
              super.paintComponent(g);
              g.setColor(Color.RED);
              g.drawString("Dimensions are " + getSize(), 10, 20);
              g.drawRect(0, 0, getMinimumSize().width-1, getMinimumSize().height-1);
              g.setColor(Color.BLUE);
              g.drawRect(0, 0, getPreferredSize().width-1, getPreferredSize().height-1);
          }
  
          @Override
          public Dimension getMinimumSize() {
              return new Dimension(200, 200);
          }
  
          @Override
          public Dimension getPreferredSize() {
              return super.getPreferredSize();
          }
      }
  }
  </code></pre>
  
  <p>Resizing the dialog triggers the ComponentListener, which explicitly sets the preferred size of the viewport view, triggering component validation. However, resizing causes jittery scroll bars. Is there a cleaner way to do this?</p>
  
  <p><strong>EDIT:</strong> thanks to camickr for the ScrollablePanel link, I've modified my JPanel class to implement Scrollable, and dynamically change the return value for getScrollableTracksViewportWidth().</p>
  
  <p>When the viewport is big, I return <code>true</code> for <code>getScrollableTracksViewportWidth()</code>, telling the JScrollPane to fill the view with my component.  When the viewport is small, I return <code>false</code>, so the scrollbars appear.</p>
  
  <pre><code>import javax.swing.*;
  import java.awt.*;
  
  public class SSBTest {
      public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                  final Component view = new MyView();
                  final JScrollPane jScrollPane = new JScrollPane(view);
                  showInDialog(jScrollPane);
              }
          });
      }
  
      private static void showInDialog(final JScrollPane jScrollPane) {
          final JDialog dialog = new JOptionPane(jScrollPane).createDialog("JScrollPane Resize Test");
          dialog.setResizable(true);
          dialog.setModal(true);
          dialog.setVisible(true);
          System.exit(0);
      }
  
      private static final class MyView extends JPanel implements Scrollable {
          @Override
          protected void paintComponent(final Graphics g) {
              super.paintComponent(g);
              g.setColor(Color.RED);
              g.drawString("MyView: " + getWidth() + "x" + getHeight(), 10, 20);
              g.drawRect(0, 0, getMinimumSize().width-1, getMinimumSize().height-1);
              g.setColor(Color.BLUE);
              g.drawRect(0, 0, getPreferredSize().width-1, getPreferredSize().height-1);
              g.drawString("Preferred/Minimum Size", 10, getPreferredSize().height/2);
              g.setColor(Color.GREEN);
              g.drawLine(0, 0, getWidth(), getHeight());
          }
  
          @Override
          public Dimension getMinimumSize() {
              return new Dimension(200, 200);
          }
  
          @Override
          public Dimension getPreferredSize() {
              return getMinimumSize();
          }
  
          public Dimension getPreferredScrollableViewportSize() {
              return getPreferredSize();
          }
  
          public int getScrollableUnitIncrement(final Rectangle visibleRect, final int orientation, final int direction) {
              return 10;
          }
  
          public int getScrollableBlockIncrement(final Rectangle visibleRect, final int orientation, final int direction) {
              return visibleRect.width;
          }
  
          public boolean getScrollableTracksViewportWidth() {
              final Container viewport = getParent();
              return viewport.getWidth() &gt; getMinimumSize().width;
          }
  
          public boolean getScrollableTracksViewportHeight() {
              return true;
          }
  
      }
  }
  </code></pre>