Search code examples
javaswingtextareanimbusscrollpane

JScrollPane doesn't show thumb in Nimbus L&F when TextArea has many lines


I've written a simple program in Netbeans 8.1 (Java Version 1.8.0_65), where it takes a list of discovered files and writes them to a jTextArea. This works fine, except when there are many lines written to the jTextArea (i.e. anything over 500 lines or so), the thumb in the scrollbar disappears. I'm seeing the exact issue that this post describes.

This appears to be a known issue with Nimbus L&F and folks have posted workarounds for this as described in the post above where they say to workaround this issue to just add the line:

UIManager.getLookAndFeelDefaults().put("ScrollBar.minimumThumbSize", new Dimension(30, 30));

The issue I am having is where exactly do I put this line of code? I've tried adding it to the initComponents right before the jScrollPanel is created (as seen below in my code). I've also tried it right after the if statement when checking if Nimbus is available:

for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                javax.swing.UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        }

I'm clearly just flailing about trying to stick it different places without understanding what I'm doing and that, to no surprise, is not working.

Can someone help me determine where this workaround line of code should go?

My Code:

    public class ViewFiles extends javax.swing.JFrame {

    /**
     * Creates new form ViewFiles
     */
    public ViewFiles() {
        initComponents();

    }

    public ViewFiles(ArrayList<DiscoveredFile> files){
        initComponents();
        discoveredFiles = files;
        displayFiles();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        javax.swing.UIManager.getLookAndFeelDefaults().put("Scrollbar.minimumThumbSize", new Dimension(30,30));
        jScrollPane1 = new javax.swing.JScrollPane();
        viewFilesTextArea = new javax.swing.JTextArea();
        viewFilesCloseButton = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);

        viewFilesTextArea.setColumns(20);
        viewFilesTextArea.setRows(5);
        jScrollPane1.setViewportView(viewFilesTextArea);

        viewFilesCloseButton.setText("Close");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 656, Short.MAX_VALUE)
                .addContainerGap())
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(viewFilesCloseButton)
                .addGap(29, 29, 29))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 277, Short.MAX_VALUE)
                .addGap(18, 18, 18)
                .addComponent(viewFilesCloseButton)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>                        

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }

        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(ViewFiles.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(ViewFiles.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(ViewFiles.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(ViewFiles.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new ViewFiles().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify                     
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JButton viewFilesCloseButton;
    private javax.swing.JTextArea viewFilesTextArea;
    // End of variables declaration                   
    private ArrayList<DiscoveredFile> discoveredFiles;

    public void displayFiles() {
        for (DiscoveredFile file : discoveredFiles){
            viewFilesTextArea.append(file.getFullPath() + "\n");
        }    
    }
}

Solution

  • Here is another approach:

    UIDefaults def = new UIDefaults();
    def.put("ScrollBar.minimumThumbSize", new Dimension(30, 30));
    jScrollPane1 = new JScrollPane();
    jScrollPane1.getVerticalScrollBar().putClientProperty("Nimbus.Overrides", def);
    
    import java.awt.*;
    import java.util.*;
    import javax.swing.*;
    
    public class ViewFiles2 extends JFrame {
      private JScrollPane jScrollPane1;
      private JButton viewFilesCloseButton;
      private JTextArea viewFilesTextArea;
      public ViewFiles2() {
        initComponents();
        displayFiles();
      }
      private void initComponents() {
          //NG?: UIManager.getLookAndFeelDefaults().put("ScrollBar.minimumThumbSize", new Dimension(30, 30));
          //OK?: UIManager.getDefaults().put("ScrollBar.minimumThumbSize", new Dimension(30, 30));
    
        jScrollPane1 = new JScrollPane();
    
        UIDefaults def = new UIDefaults();
        def.put("ScrollBar.minimumThumbSize", new Dimension(30, 30));
        jScrollPane1.getVerticalScrollBar().putClientProperty("Nimbus.Overrides", def);
    
        viewFilesTextArea = new JTextArea(20, 5);
        viewFilesCloseButton = new JButton("Close");
        jScrollPane1.setViewportView(viewFilesTextArea);
    
        GroupLayout layout = new GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
          layout.createParallelGroup(GroupLayout.Alignment.LEADING)
          .addGroup(layout.createSequentialGroup()
                    .addContainerGap()
                    .addComponent(jScrollPane1, GroupLayout.DEFAULT_SIZE, 656, Short.MAX_VALUE)
                    .addContainerGap())
          .addGroup(GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                    .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(viewFilesCloseButton)
                    .addGap(29, 29, 29))
        );
        layout.setVerticalGroup(
          layout.createParallelGroup(GroupLayout.Alignment.LEADING)
          .addGroup(layout.createSequentialGroup()
                    .addContainerGap()
                    .addComponent(jScrollPane1, GroupLayout.DEFAULT_SIZE, 277, Short.MAX_VALUE)
                    .addGap(18, 18, 18)
                    .addComponent(viewFilesCloseButton)
                    .addContainerGap())
        );
    
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        pack();
      }
      public void displayFiles() {
        viewFilesTextArea.setText(String.join("\n", Collections.nCopies(500, "aaaaaaaaaaaaa")));
      }
      public static void main(String[] args) {
        try {
          for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
              UIManager.setLookAndFeel(info.getClassName());
              break;
            }
          }
        } catch (Exception ex) {
          ex.printStackTrace();
        }
        //OK?: UIManager.getLookAndFeelDefaults().put("ScrollBar.minimumThumbSize", new Dimension(30, 30));
        //NG?: UIManager.getDefaults().put("ScrollBar.minimumThumbSize", new Dimension(30, 30));
    
        EventQueue.invokeLater(() -> {
          new ViewFiles2().setVisible(true);
        });
      }
    }