IntelliJ Themes: support customizing through properties files (issue #824)

re-written how .theme.json values are applied:
- old: .theme.json values were applied as last step to a UIDefaults object (after processing all FlatLaf properties files)
- new: .theme.json values are applied to the properties map while loading all FlatLaf properties files
This commit is contained in:
Karl Tauber
2025-03-06 13:14:04 +01:00
parent 143f96360b
commit 9b1ae5c74a
11 changed files with 870 additions and 554 deletions

View File

@@ -76,7 +76,7 @@
+ ProgressBar.foreground #c4c4c4 HSL 0 0 77 javax.swing.plaf.ColorUIResource [UI]
- ProgressBar.selectionForeground #eeeeee HSL 0 0 93 javax.swing.plaf.ColorUIResource [UI]
+ ProgressBar.selectionForeground #3c3f41 HSL 204 4 25 javax.swing.plaf.ColorUIResource [UI]
+ ProgressBar.selectionForeground #1e2021 HSL 200 5 12 javax.swing.plaf.ColorUIResource [UI]
#---- RadioButton ----
@@ -121,4 +121,4 @@
+ contrast ratio: ProgressBar.foreground #c4c4c4 #3c3f41 6.1 !
- contrast ratio: ProgressBar.selectionForeground #eeeeee #4c87c8 3.2 !!!!
+ contrast ratio: ProgressBar.selectionForeground #3c3f41 #c4c4c4 6.1 !
+ contrast ratio: ProgressBar.selectionForeground #1e2021 #c4c4c4 9.4

View File

@@ -60,6 +60,7 @@ import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicLookAndFeel;
import javax.swing.table.JTableHeader;
import com.formdev.flatlaf.*;
import com.formdev.flatlaf.demo.intellijthemes.IJThemesDump;
import com.formdev.flatlaf.intellijthemes.FlatAllIJThemes;
import com.formdev.flatlaf.testing.FlatTestLaf;
import com.formdev.flatlaf.themes.*;
@@ -155,6 +156,8 @@ public class UIDefaultsDump
private static void dumpIntelliJThemes( File dir ) {
dir = new File( dir, "intellijthemes" );
IJThemesDump.enablePropertiesRecording();
for( LookAndFeelInfo info : FlatAllIJThemes.INFOS ) {
String lafClassName = info.getClassName();
String relativeLafClassName = StringUtils.removeLeading( lafClassName, "com.formdev.flatlaf.intellijthemes." );
@@ -192,6 +195,15 @@ public class UIDefaultsDump
dump( dir, "_InputMap", lookAndFeel, defaults, key -> key.contains( "InputMap" ), false );
dumpActionMaps( dir, "_ActionMap", lookAndFeel, defaults );
}
if( lookAndFeel instanceof IntelliJTheme.ThemeLaf ) {
File cdir = new File( dir.getPath().replace( "intellijthemes", "intellijthemes-colors" ) );
dumpColorsToProperties( cdir, lookAndFeel, defaults );
// dump as .properties
File pdir = new File( dir.getPath().replace( "intellijthemes", "intellijthemes-properties" ) );
IJThemesDump.dumpProperties( pdir, lookAndFeel.getClass().getSimpleName(), defaults );
}
}
private static void dump( File dir, String nameSuffix,
@@ -231,8 +243,8 @@ public class UIDefaultsDump
if( origFile != null ) {
try {
Map<String, String> defaults1 = parse( new InputStreamReader(
new FileInputStream( origFile ), StandardCharsets.UTF_8 ) );
Map<String, String> defaults2 = parse( new StringReader( stringWriter.toString() ) );
new FileInputStream( origFile ), StandardCharsets.UTF_8 ), false );
Map<String, String> defaults2 = parse( new StringReader( stringWriter.toString() ), false );
content = diff( defaults1, defaults2 );
} catch( Exception ex ) {
@@ -280,6 +292,45 @@ public class UIDefaultsDump
}
}
private static void dumpColorsToProperties( File dir, LookAndFeel lookAndFeel, UIDefaults defaults ) {
// dump to string
StringWriter stringWriter = new StringWriter( 100000 );
new UIDefaultsDump( lookAndFeel, defaults ).dumpColorsAsProperties( new PrintWriter( stringWriter ) );
String name = lookAndFeel instanceof MyBasicLookAndFeel
? BasicLookAndFeel.class.getSimpleName()
: lookAndFeel.getClass().getSimpleName();
File file = new File( dir, name + ".properties" );
// build and append differences
if( file.exists() ) {
try {
Map<String, String> defaults1 = parse( new InputStreamReader(
new FileInputStream( file ), StandardCharsets.UTF_8 ), true );
Map<String, String> defaults2 = parse( new StringReader( stringWriter.toString() ), true );
String diff = diff( defaults1, defaults2 );
if( !diff.isEmpty() ) {
stringWriter.write( "\n\n\n\n#==== Differences ==============================================================\n\n" );
stringWriter.write( diff );
}
} catch( Exception ex ) {
ex.printStackTrace();
return;
}
}
// write to file
file.getParentFile().mkdirs();
try( Writer fileWriter = new OutputStreamWriter(
new FileOutputStream( file ), StandardCharsets.UTF_8 ) )
{
fileWriter.write( stringWriter.toString().replace( "\r", "" ) );
} catch( IOException ex ) {
ex.printStackTrace();
}
}
private static String diff( Map<String, String> defaults1, Map<String, String> defaults2 ) {
TreeSet<String> keys = new TreeSet<>();
keys.addAll( defaults1.keySet() );
@@ -355,7 +406,7 @@ public class UIDefaultsDump
buf.append( '\n' );
}
private static Map<String, String> parse( Reader in ) throws IOException {
private static Map<String, String> parse( Reader in, boolean ignoreDiffs ) throws IOException {
Map<String, String> defaults = new LinkedHashMap<>();
try( BufferedReader reader = new BufferedReader( in ) ) {
String lastKey = null;
@@ -371,6 +422,9 @@ public class UIDefaultsDump
continue;
}
if( ignoreDiffs && (trimmedLine.startsWith( "- " ) || trimmedLine.startsWith( "+ " )) )
continue;
if( Character.isWhitespace( line.charAt( 0 ) ) ) {
String value = defaults.get( lastKey );
value += '\n' + line;
@@ -443,6 +497,30 @@ public class UIDefaultsDump
dumpContrastRatios( out );
}
private void dumpColorsAsProperties( PrintWriter out ) {
defaults.keySet().stream()
.sorted( (key1, key2) -> {
return String.valueOf( key1 ).compareTo( String.valueOf( key2 ) );
} )
.forEach( key -> {
Color color = defaults.getColor( key );
if( color == null )
return;
String strKey = String.valueOf( key );
String prefix = keyPrefix( strKey );
if( !prefix.equals( lastPrefix ) ) {
lastPrefix = prefix;
out.printf( "%n%n#---- %s ----%n%n", prefix );
}
out.printf( "%-50s = #%06x", strKey, color.getRGB() & 0xffffff );
if( color.getAlpha() != 255 )
out.printf( "%02x", (color.getRGB() >> 24) & 0xff );
out.println();
} );
}
private void dumpActionMaps( PrintWriter out ) {
dumpHeader( out );