From 0ad3180b10341623ce3cf41a481e024b3f96469f Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Thu, 9 Jan 2025 12:04:25 +0100 Subject: [PATCH] FileChooser: improved performance when navigating to large directories with thousands of files (issue #953) --- CHANGELOG.md | 5 ++ .../formdev/flatlaf/ui/FlatFileChooserUI.java | 77 ++++++++++++++----- 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e24a6398..27e020a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,11 @@ FlatLaf Change Log - Extras: `FlatSVGIcon` color filters now can access painting component to implement component state based color mappings. (issue #906) +#### Fixed bugs + +- FileChooser: Improved performance when navigating to large directories with + thousands of files. (issue #953) + ## 3.5.4 diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatFileChooserUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatFileChooserUI.java index 59f360ad..9f88aee7 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatFileChooserUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatFileChooserUI.java @@ -376,31 +376,68 @@ public class FlatFileChooserUI if( icon != null ) return icon; - // get system icon - if( f != null ) { - try { - icon = getFileChooser().getFileSystemView().getSystemIcon( f ); - } catch( NullPointerException ex ) { - // Java 21 may throw a NPE for exe files that use default Windows exe icon - } + // new proxy icon + // + // Note: Since this is a super light weight icon object, we do not add it + // to the icon cache here. This keeps cache small in case of large directories + // with thousands of files when icons of all files are only needed to compute + // the layout of list/table, but never painted because located outside of visible area. + // When an icon needs to be painted, the proxy adds it to the icon cache + // and loads the real icon. + return new FlatFileViewIcon( f ); + } - if( icon != null ) { - if( icon instanceof ImageIcon ) - icon = new ScaledImageIcon( (ImageIcon) icon ); - cacheIcon( f, icon ); - return icon; - } + //---- class FlatFileViewIcon ----------------------------------------- + + /** + * A proxy icon that has a fixed (scaled) width/height (16x16) and + * gets/loads the real (system) icon only for painting. + * Avoids unnecessary getting/loading system icons. + */ + private class FlatFileViewIcon + implements Icon + { + private final File f; + private Icon realIcon; + + FlatFileViewIcon( File f ) { + this.f = f; } - // get default icon - icon = super.getIcon( f ); - - if( icon instanceof ImageIcon ) { - icon = new ScaledImageIcon( (ImageIcon) icon ); - cacheIcon( f, icon ); + @Override + public int getIconWidth() { + return UIScale.scale( 16 ); } - return icon; + @Override + public int getIconHeight() { + return UIScale.scale( 16 ); + } + + @Override + public void paintIcon( Component c, Graphics g, int x, int y ) { + // get icon on demand + if( realIcon == null ) { + // get system icon + try { + if( f != null ) + realIcon = getFileChooser().getFileSystemView().getSystemIcon( f ); + } catch( NullPointerException ex ) { + // Java 21 may throw a NPE for exe files that use default Windows exe icon + } + + // get default icon + if( realIcon == null ) + realIcon = FlatFileView.super.getIcon( f ); + + if( realIcon instanceof ImageIcon ) + realIcon = new ScaledImageIcon( (ImageIcon) realIcon ); + + cacheIcon( f, this ); + } + + realIcon.paintIcon( c, g, x, y ); + } } }