From c4b016c9c83b9456f1948dd36a0eb0faf738d974 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Wed, 29 Sep 2021 23:49:40 +0200 Subject: [PATCH] Styling: support specifying multiple style classes in single string --- .../formdev/flatlaf/FlatClientProperties.java | 4 +- .../flatlaf/ui/FlatStylingSupport.java | 7 +- .../com/formdev/flatlaf/util/StringUtils.java | 41 +++- .../flatlaf/ui/TestFlatStyleClasses.java | 22 +- .../formdev/flatlaf/util/TestStringUtils.java | 198 ++++++++++++++++++ 5 files changed, 262 insertions(+), 10 deletions(-) create mode 100644 flatlaf-core/src/test/java/com/formdev/flatlaf/util/TestStringUtils.java diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java index e6aa98f0..542fe963 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java @@ -146,8 +146,8 @@ public interface FlatClientProperties String STYLE = "FlatLaf.style"; /** - * Specifies the style class(es) of a component as String - * or as {@link java.util.List}<String>. + * Specifies the style class(es) of a component as String (single class or multiple classes separated by space characters) + * or as {@code String[]} or {@link java.util.List}<String> (multiple classes). *

* The style rules must be defined in UI defaults either as strings (in CSS syntax) * or as {@link java.util.Map}<String, Object> (with binary values). diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatStylingSupport.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatStylingSupport.java index de2b9d5c..6f21ba2b 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatStylingSupport.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatStylingSupport.java @@ -127,7 +127,7 @@ public class FlatStylingSupport * UIManager.get( "[style]Button.foo" ) ); * } * - * @param styleClass the style class(es) either as string (single class) + * @param styleClass the style class(es) either as string (single class or multiple classes separated by space characters) * or as {@code String[]} or {@link java.util.List}<String> (multiple classes) * @param type the type of the component * @return the styles @@ -136,8 +136,11 @@ public class FlatStylingSupport if( styleClass == null ) return null; + if( styleClass instanceof String && ((String)styleClass).indexOf( ' ' ) >= 0 ) + styleClass = StringUtils.split( (String) styleClass, ' ', true, true ); + if( styleClass instanceof String ) - return getStyleForClass( (String) styleClass, type ); + return getStyleForClass( ((String)styleClass).trim(), type ); else if( styleClass instanceof String[] ) { Object style = null; for( String cls : (String[]) styleClass ) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/StringUtils.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/StringUtils.java index df184d1e..2f952014 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/StringUtils.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/StringUtils.java @@ -17,6 +17,7 @@ package com.formdev.flatlaf.util; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -46,16 +47,50 @@ public class StringUtils } public static List split( String str, char delim ) { - ArrayList strs = new ArrayList<>(); + return split( str, delim, false, false ); + } + + /** + * Splits a string at the specified delimiter. + * If trimming is enabled, then leading and trailing whitespace characters are removed. + * If excludeEmpty is {@code true}, then only non-empty strings are returned. + * + * @since 2 + */ + public static List split( String str, char delim, boolean trim, boolean excludeEmpty ) { int delimIndex = str.indexOf( delim ); + if( delimIndex < 0 ) { + if( trim ) + str = str.trim(); + return !excludeEmpty || !str.isEmpty() + ? Collections.singletonList( str ) + : Collections.emptyList(); + } + + ArrayList strs = new ArrayList<>(); int index = 0; while( delimIndex >= 0 ) { - strs.add( str.substring( index, delimIndex ) ); + add( strs, str, index, delimIndex, trim, excludeEmpty ); index = delimIndex + 1; delimIndex = str.indexOf( delim, index ); } - strs.add( str.substring( index ) ); + add( strs, str, index, str.length(), trim, excludeEmpty ); return strs; } + + private static void add( List strs, String str, int begin, int end, boolean trim, boolean excludeEmpty ) { + if( trim ) { + // skip leading whitespace + while( begin < end && str.charAt( begin ) <= ' ' ) + begin++; + + // skip trailing whitespace + while( begin < end && str.charAt( end - 1 ) <= ' ' ) + end--; + } + + if( !excludeEmpty || end > begin ) + strs.add( str.substring( begin, end ) ); + } } diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleClasses.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleClasses.java index d96036f1..20bb9ebe 100644 --- a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleClasses.java +++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleClasses.java @@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui; import static org.junit.jupiter.api.Assertions.assertEquals; import java.awt.Color; import java.awt.Dimension; +import java.util.Arrays; import javax.swing.*; import javax.swing.table.JTableHeader; import org.junit.jupiter.api.AfterAll; @@ -114,10 +115,25 @@ public class TestFlatStyleClasses assertEquals( FlatStylingSupport.concatStyles( BUTTON_PRIMARY, SECONDARY ), - FlatStylingSupport.getStyleForClasses( new String[] { "primary", "secondary" }, "Button" ) ); + FlatStylingSupport.getStyleForClasses( "primary secondary", "Button" ) ); + assertEquals( + FlatStylingSupport.concatStyles( SECONDARY, BUTTON_PRIMARY ), + FlatStylingSupport.getStyleForClasses( "secondary primary", "Button" ) ); + + // String + assertEquals( + FlatStylingSupport.concatStyles( SECONDARY, BUTTON_PRIMARY ), + FlatStylingSupport.getStyleForClasses( " secondary primary bla blu ", "Button" ) ); + + // String[] assertEquals( FlatStylingSupport.concatStyles( SECONDARY, BUTTON_PRIMARY ), FlatStylingSupport.getStyleForClasses( new String[] { "secondary", "primary" }, "Button" ) ); + + // List + assertEquals( + FlatStylingSupport.concatStyles( SECONDARY, BUTTON_PRIMARY ), + FlatStylingSupport.getStyleForClasses( Arrays.asList( "secondary", "primary" ), "Button" ) ); } @Test @@ -132,7 +148,7 @@ public class TestFlatStyleClasses @Test void apply2() { JButton c = new JButton(); - c.putClientProperty( FlatClientProperties.STYLE_CLASS, new String[] { "primary", "secondary" } ); + c.putClientProperty( FlatClientProperties.STYLE_CLASS, "primary secondary" ); assertEquals( new Color( 0x00ff88 ), c.getBackground() ); assertEquals( Color.white, c.getForeground() ); @@ -141,7 +157,7 @@ public class TestFlatStyleClasses @Test void apply3() { JButton c = new JButton(); - c.putClientProperty( FlatClientProperties.STYLE_CLASS, new String[] { "secondary", "primary" } ); + c.putClientProperty( FlatClientProperties.STYLE_CLASS, "secondary primary" ); assertEquals( new Color( 0x0088ff ), c.getBackground() ); assertEquals( Color.white, c.getForeground() ); diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/util/TestStringUtils.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/util/TestStringUtils.java new file mode 100644 index 00000000..136ebbcf --- /dev/null +++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/util/TestStringUtils.java @@ -0,0 +1,198 @@ +/* + * Copyright 2021 FormDev Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.formdev.flatlaf.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +/** + * @author Karl Tauber + */ +public class TestStringUtils +{ + @Test + void split() { + // empty + assertEquals( + Arrays.asList( "" ), + StringUtils.split( "", ',' ) ); + + // not empty + assertEquals( + Arrays.asList( "a" ), + StringUtils.split( "a", ',' ) ); + assertEquals( + Arrays.asList( "a", "b" ), + StringUtils.split( "a,b", ',' ) ); + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( "a,b,c", ',' ) ); + + // empty parts + assertEquals( + Arrays.asList( "", "b", "c" ), + StringUtils.split( ",b,c", ',' ) ); + assertEquals( + Arrays.asList( "a", "", "c" ), + StringUtils.split( "a,,c", ',' ) ); + assertEquals( + Arrays.asList( "a", "b", "" ), + StringUtils.split( "a,b,", ',' ) ); + + // parts with leading/trailing spaces + assertEquals( + Arrays.asList( "a", " b", " c" ), + StringUtils.split( "a, b, c", ',' ) ); + assertEquals( + Arrays.asList( " a", "b", "c " ), + StringUtils.split( " a,b,c ", ',' ) ); + assertEquals( + Arrays.asList( " a", " b ", "c " ), + StringUtils.split( " a, b ,c ", ',' ) ); + + // space delimiter + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( "a b c", ' ' ) ); + assertEquals( + Arrays.asList( "a", "", "", "b", "", "c" ), + StringUtils.split( "a b c", ' ' ) ); + + // new line delimiter + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( "a\nb\nc", '\n' ) ); + assertEquals( + Arrays.asList( "a", "", "", "b", "", "c" ), + StringUtils.split( "a\n\n\nb\n\nc", '\n' ) ); + } + + @Test + void splitTrim() { + // empty + assertEquals( + Arrays.asList( "" ), + StringUtils.split( "", ',', true, false ) ); + + // not empty + assertEquals( + Arrays.asList( "a" ), + StringUtils.split( "a", ',', true, false ) ); + assertEquals( + Arrays.asList( "a", "b" ), + StringUtils.split( "a,b", ',', true, false ) ); + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( "a,b,c", ',', true, false ) ); + + // empty parts + assertEquals( + Arrays.asList( "", "b", "c" ), + StringUtils.split( ",b,c", ',', true, false ) ); + assertEquals( + Arrays.asList( "a", "", "c" ), + StringUtils.split( "a,,c", ',', true, false ) ); + assertEquals( + Arrays.asList( "a", "b", "" ), + StringUtils.split( "a,b,", ',', true, false ) ); + + // parts with leading/trailing spaces + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( "a, b, c", ',', true, false ) ); + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( " a,b,c ", ',', true, false ) ); + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( " a, b ,c ", ',', true, false ) ); + + // space delimiter + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( "a b c", ' ', true, false ) ); + assertEquals( + Arrays.asList( "a", "", "", "b", "", "c" ), + StringUtils.split( "a b c", ' ', true, false ) ); + + // new line delimiter + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( "a\nb\nc", '\n', true, false ) ); + assertEquals( + Arrays.asList( "a", "", "", "b", "", "c" ), + StringUtils.split( "a\n\n\nb\n\nc", '\n', true, false ) ); + } + + @Test + void splitTrimAndExcludeEmpty() { + // empty + assertEquals( + Arrays.asList(), + StringUtils.split( "", ',', true, true ) ); + + // not empty + assertEquals( + Arrays.asList( "a" ), + StringUtils.split( "a", ',', true, true ) ); + assertEquals( + Arrays.asList( "a", "b" ), + StringUtils.split( "a,b", ',', true, true ) ); + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( "a,b,c", ',', true, true ) ); + + // empty parts + assertEquals( + Arrays.asList( "b", "c" ), + StringUtils.split( ",b,c", ',', true, true ) ); + assertEquals( + Arrays.asList( "a", "c" ), + StringUtils.split( "a,,c", ',', true, true ) ); + assertEquals( + Arrays.asList( "a", "b" ), + StringUtils.split( "a,b,", ',', true, true ) ); + + // parts with leading/trailing spaces + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( "a, b, c", ',', true, true ) ); + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( " a,b,c ", ',', true, true ) ); + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( " a, b ,c ", ',', true, true ) ); + + // space delimiter + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( "a b c", ' ', true, true ) ); + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( "a b c", ' ', true, true ) ); + + // new line delimiter + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( "a\nb\nc", '\n', true, true ) ); + assertEquals( + Arrays.asList( "a", "b", "c" ), + StringUtils.split( "a\n\n\nb\n\nc", '\n', true, true ) ); + } +}