Demo: support using own FlatLaf themes (.properties files) that are located in working directory of Demo application

This commit is contained in:
Karl Tauber
2020-03-29 18:02:35 +02:00
parent 1bebfe9cf2
commit 5ed40cab1d
8 changed files with 159 additions and 32 deletions

View File

@@ -7,6 +7,9 @@ FlatLaf Change Log
on JetBrains Runtime. (issue #69) on JetBrains Runtime. (issue #69)
- Tree: Fixed repainting wide selection on focus gained/lost. - Tree: Fixed repainting wide selection on focus gained/lost.
- No longer use system property `sun.java2d.uiScale`. (Java 8 only) - No longer use system property `sun.java2d.uiScale`. (Java 8 only)
- Demo: Support using own FlatLaf themes (`.properties` files) that are located
in working directory of Demo application. Shown in the "Themes" list under
category "Current Directory".
## 0.28 ## 0.28

View File

@@ -34,6 +34,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
@@ -278,9 +279,9 @@ public abstract class FlatLaf
// load defaults from properties // load defaults from properties
List<Class<?>> lafClassesForDefaultsLoading = getLafClassesForDefaultsLoading(); List<Class<?>> lafClassesForDefaultsLoading = getLafClassesForDefaultsLoading();
if( lafClassesForDefaultsLoading != null ) if( lafClassesForDefaultsLoading != null )
UIDefaultsLoader.loadDefaultsFromProperties( lafClassesForDefaultsLoading, addons, defaults ); UIDefaultsLoader.loadDefaultsFromProperties( lafClassesForDefaultsLoading, addons, getAdditionalDefaults(), defaults );
else else
UIDefaultsLoader.loadDefaultsFromProperties( getClass(), addons, defaults ); UIDefaultsLoader.loadDefaultsFromProperties( getClass(), addons, getAdditionalDefaults(), defaults );
// use Aqua MenuBarUI if Mac screen menubar is enabled // use Aqua MenuBarUI if Mac screen menubar is enabled
if( SystemInfo.IS_MAC && Boolean.getBoolean( "apple.laf.useScreenMenuBar" ) ) if( SystemInfo.IS_MAC && Boolean.getBoolean( "apple.laf.useScreenMenuBar" ) )
@@ -307,7 +308,11 @@ public abstract class FlatLaf
void applyAdditionalDefaults( UIDefaults defaults ) { void applyAdditionalDefaults( UIDefaults defaults ) {
} }
List<Class<?>> getLafClassesForDefaultsLoading() { protected List<Class<?>> getLafClassesForDefaultsLoading() {
return null;
}
protected Properties getAdditionalDefaults() {
return null; return null;
} }

View File

@@ -518,7 +518,7 @@ public class IntelliJTheme
} }
@Override @Override
ArrayList<Class<?>> getLafClassesForDefaultsLoading() { protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
ArrayList<Class<?>> lafClasses = new ArrayList<>(); ArrayList<Class<?>> lafClasses = new ArrayList<>();
lafClasses.add( FlatLaf.class ); lafClasses.add( FlatLaf.class );
lafClasses.add( theme.dark ? FlatDarkLaf.class : FlatLightLaf.class ); lafClasses.add( theme.dark ? FlatDarkLaf.class : FlatLightLaf.class );

View File

@@ -69,7 +69,7 @@ class UIDefaultsLoader
private static final String GLOBAL_PREFIX = "*."; private static final String GLOBAL_PREFIX = "*.";
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons, static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons,
UIDefaults defaults ) Properties additionalDefaults, UIDefaults defaults )
{ {
// determine classes in class hierarchy in reverse order // determine classes in class hierarchy in reverse order
ArrayList<Class<?>> lafClasses = new ArrayList<>(); ArrayList<Class<?>> lafClasses = new ArrayList<>();
@@ -80,11 +80,11 @@ class UIDefaultsLoader
lafClasses.add( 0, lafClass ); lafClasses.add( 0, lafClass );
} }
loadDefaultsFromProperties( lafClasses, addons, defaults ); loadDefaultsFromProperties( lafClasses, addons, additionalDefaults, defaults );
} }
static void loadDefaultsFromProperties( List<Class<?>> lafClasses, List<FlatDefaultsAddon> addons, static void loadDefaultsFromProperties( List<Class<?>> lafClasses, List<FlatDefaultsAddon> addons,
UIDefaults defaults ) Properties additionalDefaults, UIDefaults defaults )
{ {
try { try {
// load core properties files // load core properties files
@@ -115,6 +115,10 @@ class UIDefaultsLoader
addonClassLoaders.add( addonClassLoader ); addonClassLoaders.add( addonClassLoader );
} }
// add additional defaults
if( additionalDefaults != null )
properties.putAll( additionalDefaults );
// collect all platform specific keys (but do not modify properties) // collect all platform specific keys (but do not modify properties)
ArrayList<String> platformSpecificKeys = new ArrayList<>(); ArrayList<String> platformSpecificKeys = new ArrayList<>();
for( Object key : properties.keySet() ) { for( Object key : properties.keySet() ) {

View File

@@ -0,0 +1,13 @@
# This file demonstrates using a FlatLaf theme file in the FlatLaf Demo application.
# Must be in the working directory of the Demo application.
# Shown in the "Themes" list under category "Current Directory".
#
# Modifications to this file are automatically loaded by the FlatLaf Demo application
# when the Demo window is activated.
# base theme (light, dark, intellij or darcula)
@baseTheme=light
# add you theme defaults here
@background=#ccc

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.demo; package com.formdev.flatlaf.demo;
import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
import javax.swing.UIManager; import javax.swing.UIManager;
@@ -23,6 +24,8 @@ import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf; import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.IntelliJTheme; import com.formdev.flatlaf.IntelliJTheme;
import com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel; import com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel;
import com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel.PropertiesLaf;
import com.formdev.flatlaf.util.StringUtils;
/** /**
* @author Karl Tauber * @author Karl Tauber
@@ -30,12 +33,12 @@ import com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel;
public class DemoPrefs public class DemoPrefs
{ {
public static final String KEY_LAF = "laf"; public static final String KEY_LAF = "laf";
public static final String KEY_LAF_INTELLIJ_THEME = "lafIntelliJTheme"; public static final String KEY_LAF_THEME = "lafTheme";
public static final String RESOURCE_PREFIX = "res:"; public static final String RESOURCE_PREFIX = "res:";
public static final String FILE_PREFIX = "file:"; public static final String FILE_PREFIX = "file:";
public static final String INTELLIJ_THEME_UI_KEY = "__FlatLaf.demo.intelliJTheme"; public static final String THEME_UI_KEY = "__FlatLaf.demo.theme";
private static Preferences state; private static Preferences state;
@@ -55,16 +58,27 @@ public class DemoPrefs
else { else {
String lafClassName = state.get( KEY_LAF, FlatLightLaf.class.getName() ); String lafClassName = state.get( KEY_LAF, FlatLightLaf.class.getName() );
if( IntelliJTheme.ThemeLaf.class.getName().equals( lafClassName ) ) { if( IntelliJTheme.ThemeLaf.class.getName().equals( lafClassName ) ) {
String intelliJTheme = state.get( KEY_LAF_INTELLIJ_THEME, "" ); String theme = state.get( KEY_LAF_THEME, "" );
if( intelliJTheme.startsWith( RESOURCE_PREFIX ) ) if( theme.startsWith( RESOURCE_PREFIX ) )
IntelliJTheme.install( IJThemesPanel.class.getResourceAsStream( intelliJTheme.substring( RESOURCE_PREFIX.length() ) ) ); IntelliJTheme.install( IJThemesPanel.class.getResourceAsStream( theme.substring( RESOURCE_PREFIX.length() ) ) );
else if( intelliJTheme.startsWith( FILE_PREFIX ) ) else if( theme.startsWith( FILE_PREFIX ) )
FlatLaf.install( IntelliJTheme.createLaf( new FileInputStream( intelliJTheme.substring( FILE_PREFIX.length() ) ) ) ); FlatLaf.install( IntelliJTheme.createLaf( new FileInputStream( theme.substring( FILE_PREFIX.length() ) ) ) );
else else
FlatLightLaf.install(); FlatLightLaf.install();
if( !intelliJTheme.isEmpty() ) if( !theme.isEmpty() )
UIManager.getLookAndFeelDefaults().put( INTELLIJ_THEME_UI_KEY, intelliJTheme ); UIManager.getLookAndFeelDefaults().put( THEME_UI_KEY, theme );
} else if( IJThemesPanel.PropertiesLaf.class.getName().equals( lafClassName ) ) {
String theme = state.get( KEY_LAF_THEME, "" );
if( theme.startsWith( FILE_PREFIX ) ) {
File themeFile = new File( theme.substring( FILE_PREFIX.length() ) );
String themeName = StringUtils.removeTrailing( themeFile.getName(), ".properties" );
FlatLaf.install( new PropertiesLaf( themeName, themeFile ) );
} else
FlatLightLaf.install();
if( !theme.isEmpty() )
UIManager.getLookAndFeelDefaults().put( THEME_UI_KEY, theme );
} else } else
UIManager.setLookAndFeel( lafClassName ); UIManager.setLookAndFeel( lafClassName );
} }

View File

@@ -68,7 +68,9 @@ class IJThemesManager
// get current working directory // get current working directory
File directory = new File( "" ).getAbsoluteFile(); File directory = new File( "" ).getAbsoluteFile();
File[] themeFiles = directory.listFiles( (dir, name) -> name.endsWith( ".theme.json" ) ); File[] themeFiles = directory.listFiles( (dir, name) -> {
return name.endsWith( ".theme.json" ) || name.endsWith( ".properties" );
} );
if( themeFiles == null ) if( themeFiles == null )
return; return;
@@ -77,7 +79,10 @@ class IJThemesManager
moreThemes.clear(); moreThemes.clear();
for( File f : themeFiles ) { for( File f : themeFiles ) {
String name = StringUtils.removeTrailing( f.getName(), ".theme.json" ); String fname = f.getName();
String name = fname.endsWith( ".properties" )
? StringUtils.removeTrailing( fname, ".properties" )
: StringUtils.removeTrailing( fname, ".theme.json" );
moreThemes.add( new IJThemeInfo( name, null, null, null, null, null, f, null ) ); moreThemes.add( new IJThemeInfo( name, null, null, null, null, null, f, null ) );
lastModifiedMap.put( f, f.lastModified() ); lastModifiedMap.put( f, f.lastModified() );
} }

View File

@@ -28,6 +28,7 @@ import java.beans.PropertyChangeListener;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.file.Files; import java.nio.file.Files;
@@ -37,6 +38,7 @@ import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Properties;
import java.util.function.Predicate; import java.util.function.Predicate;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.CompoundBorder; import javax.swing.border.CompoundBorder;
@@ -71,6 +73,7 @@ public class IJThemesPanel
private Window window; private Window window;
private File lastDirectory; private File lastDirectory;
private boolean isAdjustingThemesList;
public IJThemesPanel() { public IJThemesPanel() {
initComponents(); initComponents();
@@ -134,6 +137,10 @@ public class IJThemesPanel
themes.add( new IJThemeInfo( "Flat IntelliJ", null, null, null, null, null, null, FlatIntelliJLaf.class.getName() ) ); themes.add( new IJThemeInfo( "Flat IntelliJ", null, null, null, null, null, null, FlatIntelliJLaf.class.getName() ) );
themes.add( new IJThemeInfo( "Flat Darcula", null, null, null, null, null, null, FlatDarculaLaf.class.getName() ) ); themes.add( new IJThemeInfo( "Flat Darcula", null, null, null, null, null, null, FlatDarculaLaf.class.getName() ) );
// add themes from directory
categories.put( themes.size(), "Current Directory" );
themes.addAll( themesManager.moreThemes );
// add uncategorized bundled themes // add uncategorized bundled themes
categories.put( themes.size(), "IntelliJ Themes" ); categories.put( themes.size(), "IntelliJ Themes" );
for( IJThemeInfo ti : themesManager.bundledThemes ) { for( IJThemeInfo ti : themesManager.bundledThemes ) {
@@ -157,10 +164,6 @@ public class IJThemesPanel
themes.add( ti ); themes.add( ti );
} }
// add themes from directory
categories.put( themes.size(), "Current Directory" );
themes.addAll( themesManager.moreThemes );
// remember selection // remember selection
IJThemeInfo oldSel = themesList.getSelectedValue(); IJThemeInfo oldSel = themesList.getSelectedValue();
@@ -193,7 +196,7 @@ public class IJThemesPanel
} }
private void themesListValueChanged( ListSelectionEvent e ) { private void themesListValueChanged( ListSelectionEvent e ) {
if( e.getValueIsAdjusting() ) if( e.getValueIsAdjusting() || isAdjustingThemesList )
return; return;
IJThemeInfo themeInfo = themesList.getSelectedValue(); IJThemeInfo themeInfo = themesList.getSelectedValue();
@@ -223,15 +226,19 @@ public class IJThemesPanel
} }
} else if( themeInfo.themeFile != null ) { } else if( themeInfo.themeFile != null ) {
try { try {
FlatLaf.install( IntelliJTheme.createLaf( new FileInputStream( themeInfo.themeFile ) ) ); if( themeInfo.themeFile.getName().endsWith( ".properties" ) ) {
DemoPrefs.getState().put( DemoPrefs.KEY_LAF_INTELLIJ_THEME, DemoPrefs.FILE_PREFIX + themeInfo.themeFile ); FlatLaf.install( new PropertiesLaf( themeInfo.name, themeInfo.themeFile ) );
} else
FlatLaf.install( IntelliJTheme.createLaf( new FileInputStream( themeInfo.themeFile ) ) );
DemoPrefs.getState().put( DemoPrefs.KEY_LAF_THEME, DemoPrefs.FILE_PREFIX + themeInfo.themeFile );
} catch( Exception ex ) { } catch( Exception ex ) {
ex.printStackTrace(); ex.printStackTrace();
showInformationDialog( "Failed to load '" + themeInfo.themeFile + "'.", ex ); showInformationDialog( "Failed to load '" + themeInfo.themeFile + "'.", ex );
} }
} else { } else {
IntelliJTheme.install( getClass().getResourceAsStream( themeInfo.resourceName ) ); IntelliJTheme.install( getClass().getResourceAsStream( themeInfo.resourceName ) );
DemoPrefs.getState().put( DemoPrefs.KEY_LAF_INTELLIJ_THEME, DemoPrefs.RESOURCE_PREFIX + themeInfo.resourceName ); DemoPrefs.getState().put( DemoPrefs.KEY_LAF_THEME, DemoPrefs.RESOURCE_PREFIX + themeInfo.resourceName );
} }
// update all components // update all components
@@ -331,17 +338,17 @@ public class IJThemesPanel
private void selectedCurrentLookAndFeel() { private void selectedCurrentLookAndFeel() {
LookAndFeel lookAndFeel = UIManager.getLookAndFeel(); LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
String intelliJTheme = UIManager.getLookAndFeelDefaults().getString( DemoPrefs.INTELLIJ_THEME_UI_KEY ); String theme = UIManager.getLookAndFeelDefaults().getString( DemoPrefs.THEME_UI_KEY );
if( intelliJTheme == null && lookAndFeel instanceof IntelliJTheme.ThemeLaf ) if( theme == null && (lookAndFeel instanceof IntelliJTheme.ThemeLaf || lookAndFeel instanceof PropertiesLaf) )
return; return;
Predicate<IJThemeInfo> test; Predicate<IJThemeInfo> test;
if( intelliJTheme != null && intelliJTheme.startsWith( DemoPrefs.RESOURCE_PREFIX ) ) { if( theme != null && theme.startsWith( DemoPrefs.RESOURCE_PREFIX ) ) {
String resourceName = intelliJTheme.substring( DemoPrefs.RESOURCE_PREFIX.length() ); String resourceName = theme.substring( DemoPrefs.RESOURCE_PREFIX.length() );
test = ti -> Objects.equals( ti.resourceName, resourceName ); test = ti -> Objects.equals( ti.resourceName, resourceName );
} else if( intelliJTheme != null && intelliJTheme.startsWith( DemoPrefs.FILE_PREFIX ) ) { } else if( theme != null && theme.startsWith( DemoPrefs.FILE_PREFIX ) ) {
File themeFile = new File( intelliJTheme.substring( DemoPrefs.FILE_PREFIX.length() ) ); File themeFile = new File( theme.substring( DemoPrefs.FILE_PREFIX.length() ) );
test = ti -> Objects.equals( ti.themeFile, themeFile ); test = ti -> Objects.equals( ti.themeFile, themeFile );
} else { } else {
String lafClassName = lookAndFeel.getClass().getName(); String lafClassName = lookAndFeel.getClass().getName();
@@ -356,11 +363,13 @@ public class IJThemesPanel
} }
} }
isAdjustingThemesList = true;
if( newSel >= 0 ) { if( newSel >= 0 ) {
if( newSel != themesList.getSelectedIndex() ) if( newSel != themesList.getSelectedIndex() )
themesList.setSelectedIndex( newSel ); themesList.setSelectedIndex( newSel );
} else } else
themesList.clearSelection(); themesList.clearSelection();
isAdjustingThemesList = false;
} }
private void initComponents() { private void initComponents() {
@@ -420,4 +429,78 @@ public class IJThemesPanel
private JScrollPane themesScrollPane; private JScrollPane themesScrollPane;
private JList<IJThemeInfo> themesList; private JList<IJThemeInfo> themesList;
// JFormDesigner - End of variables declaration //GEN-END:variables // JFormDesigner - End of variables declaration //GEN-END:variables
//---- class PropertiesLaf ------------------------------------------------
public static class PropertiesLaf
extends FlatLaf
{
private final String name;
private final String baseTheme;
private final boolean dark;
private final Properties properties;
public PropertiesLaf( String name, File propertiesFile )
throws IOException
{
this.name = name;
properties = new Properties();
try( InputStream in = new FileInputStream( propertiesFile ) ) {
if( in != null )
properties.load( in );
}
baseTheme = properties.getProperty( "@baseTheme", "light" );
dark = "dark".equalsIgnoreCase( baseTheme ) || "darcula".equalsIgnoreCase( baseTheme );
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return name;
}
@Override
public boolean isDark() {
return dark;
}
@Override
protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
ArrayList<Class<?>> lafClasses = new ArrayList<>();
lafClasses.add( FlatLaf.class );
switch( baseTheme.toLowerCase() ) {
default:
case "light":
lafClasses.add( FlatLightLaf.class );
break;
case "dark":
lafClasses.add( FlatDarkLaf.class );
break;
case "intellij":
lafClasses.add( FlatLightLaf.class );
lafClasses.add( FlatIntelliJLaf.class );
break;
case "darcula":
lafClasses.add( FlatDarkLaf.class );
lafClasses.add( FlatDarculaLaf.class );
break;
}
lafClasses.add( PropertiesLaf.class );
return lafClasses;
}
@Override
protected Properties getAdditionalDefaults() {
return properties;
}
}
} }