mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-11 06:27:13 -06:00
Theme Editor: added token maker (based on .properties token maker)
This commit is contained in:
@@ -21,7 +21,9 @@ import java.awt.Font;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;
|
||||||
import org.fife.ui.rsyntaxtextarea.FileLocation;
|
import org.fife.ui.rsyntaxtextarea.FileLocation;
|
||||||
|
import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
|
||||||
import org.fife.ui.rtextarea.RTextScrollPane;
|
import org.fife.ui.rtextarea.RTextScrollPane;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -33,14 +35,21 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
class FlatThemeEditorPane
|
class FlatThemeEditorPane
|
||||||
extends JPanel
|
extends JPanel
|
||||||
{
|
{
|
||||||
|
private static final String FLATLAF_STYLE = "text/flatlaf";
|
||||||
|
|
||||||
private final RTextScrollPane scrollPane;
|
private final RTextScrollPane scrollPane;
|
||||||
private final FlatSyntaxTextArea textArea;
|
private final FlatSyntaxTextArea textArea;
|
||||||
|
|
||||||
FlatThemeEditorPane() {
|
FlatThemeEditorPane() {
|
||||||
super( new BorderLayout() );
|
super( new BorderLayout() );
|
||||||
|
|
||||||
|
// register FlatLaf token maker
|
||||||
|
AbstractTokenMakerFactory tmf = (AbstractTokenMakerFactory) TokenMakerFactory.getDefaultInstance();
|
||||||
|
tmf.putMapping( FLATLAF_STYLE, FlatThemeTokenMaker.class.getName() );
|
||||||
|
|
||||||
// create text area
|
// create text area
|
||||||
textArea = new FlatSyntaxTextArea();
|
textArea = new FlatSyntaxTextArea();
|
||||||
|
textArea.setSyntaxEditingStyle( FLATLAF_STYLE );
|
||||||
|
|
||||||
// create scroll pane
|
// create scroll pane
|
||||||
scrollPane = new RTextScrollPane( textArea );
|
scrollPane = new RTextScrollPane( textArea );
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class FlatThemeFileEditor
|
|||||||
public static void main( String[] args ) {
|
public static void main( String[] args ) {
|
||||||
File file = new File( args.length > 0
|
File file = new File( args.length > 0
|
||||||
? args[0]
|
? args[0]
|
||||||
: "../flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties" ); // TODO
|
: "theme-editor-test.properties" ); // TODO
|
||||||
|
|
||||||
SwingUtilities.invokeLater( () -> {
|
SwingUtilities.invokeLater( () -> {
|
||||||
FlatLightLaf.install();
|
FlatLightLaf.install();
|
||||||
|
|||||||
@@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 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.themeeditor;
|
||||||
|
|
||||||
|
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
|
||||||
|
import org.fife.ui.rsyntaxtextarea.Token;
|
||||||
|
import org.fife.ui.rsyntaxtextarea.TokenMap;
|
||||||
|
import org.fife.ui.rsyntaxtextarea.TokenTypes;
|
||||||
|
import org.fife.ui.rsyntaxtextarea.modes.PropertiesFileTokenMaker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token maker for FlatLaf properties files.
|
||||||
|
* <p>
|
||||||
|
* Lets the super class parse the properties file and modify the added tokens.
|
||||||
|
* The super class uses {@link TokenTypes#RESERVED_WORD} for property keys and
|
||||||
|
* {@link TokenTypes#LITERAL_STRING_DOUBLE_QUOTE} for property values.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatThemeTokenMaker
|
||||||
|
extends PropertiesFileTokenMaker
|
||||||
|
{
|
||||||
|
private static final int TOKEN_PROPERTY = Token.IDENTIFIER;
|
||||||
|
private static final int TOKEN_VARIABLE = Token.VARIABLE;
|
||||||
|
private static final int TOKEN_NUMBER = Token.LITERAL_NUMBER_DECIMAL_INT;
|
||||||
|
private static final int TOKEN_COLOR = Token.LITERAL_NUMBER_HEXADECIMAL;
|
||||||
|
private static final int TOKEN_STRING = Token.LITERAL_STRING_DOUBLE_QUOTE;
|
||||||
|
private static final int TOKEN_FUNCTION = Token.FUNCTION;
|
||||||
|
private static final int TOKEN_TYPE = Token.DATA_TYPE;
|
||||||
|
|
||||||
|
private final TokenMap tokenMap = new TokenMap();
|
||||||
|
|
||||||
|
public FlatThemeTokenMaker() {
|
||||||
|
// null, false, true
|
||||||
|
tokenMap.put( "null", Token.RESERVED_WORD );
|
||||||
|
tokenMap.put( "false", Token.LITERAL_BOOLEAN );
|
||||||
|
tokenMap.put( "true", Token.LITERAL_BOOLEAN );
|
||||||
|
|
||||||
|
// functions
|
||||||
|
tokenMap.put( "rgb", TOKEN_FUNCTION );
|
||||||
|
tokenMap.put( "rgba", TOKEN_FUNCTION );
|
||||||
|
tokenMap.put( "hsl", TOKEN_FUNCTION );
|
||||||
|
tokenMap.put( "hsla", TOKEN_FUNCTION );
|
||||||
|
tokenMap.put( "lighten", TOKEN_FUNCTION );
|
||||||
|
tokenMap.put( "darken", TOKEN_FUNCTION );
|
||||||
|
tokenMap.put( "lazy", TOKEN_FUNCTION );
|
||||||
|
|
||||||
|
// function options
|
||||||
|
tokenMap.put( "relative", Token.RESERVED_WORD );
|
||||||
|
tokenMap.put( "autoInverse", Token.RESERVED_WORD );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is only invoked from the super class.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void addToken( char[] array, int start, int end, int tokenType, int startOffset, boolean hyperlink ) {
|
||||||
|
// debugInputToken( array, start, end, tokenType, startOffset, hyperlink );
|
||||||
|
|
||||||
|
// ignore invalid token
|
||||||
|
if( end < start )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( tokenType == Token.RESERVED_WORD ) {
|
||||||
|
// key
|
||||||
|
int newTokenType = (array[start] == '@') ? TOKEN_VARIABLE : TOKEN_PROPERTY;
|
||||||
|
super.addToken( array, start, end, newTokenType, startOffset, hyperlink );
|
||||||
|
} else if( tokenType == Token.LITERAL_STRING_DOUBLE_QUOTE ) {
|
||||||
|
// value
|
||||||
|
tokenizeValue( array, start, end, startOffset );
|
||||||
|
} else if( tokenType == Token.VARIABLE ) {
|
||||||
|
// '{variable}'
|
||||||
|
super.addToken( array, start, end, TOKEN_TYPE, startOffset, hyperlink );
|
||||||
|
} else {
|
||||||
|
// comments or operators
|
||||||
|
super.addToken( array, start, end, tokenType, startOffset, hyperlink );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tokenizeValue( char[] array, int start, int end, int startOffset ) {
|
||||||
|
int newStartOffset = startOffset - start;
|
||||||
|
|
||||||
|
int currentTokenStart = start;
|
||||||
|
int currentTokenType = Token.NULL;
|
||||||
|
|
||||||
|
for( int i = start; i <= end; i++ ) {
|
||||||
|
int newTokenType;
|
||||||
|
char ch = array[i];
|
||||||
|
if( ch <= ' ' )
|
||||||
|
newTokenType = Token.WHITESPACE;
|
||||||
|
else if( ch == '#' || (currentTokenType == TOKEN_COLOR && RSyntaxUtilities.isHexCharacter( ch )) )
|
||||||
|
newTokenType = TOKEN_COLOR;
|
||||||
|
else if( ch == '$' || (currentTokenType == TOKEN_PROPERTY && isPropertyChar( ch )) )
|
||||||
|
newTokenType = TOKEN_PROPERTY;
|
||||||
|
else if( ch == '@' || (currentTokenType == TOKEN_VARIABLE && isPropertyChar( ch )) )
|
||||||
|
newTokenType = TOKEN_VARIABLE;
|
||||||
|
else if( currentTokenType != TOKEN_STRING && (RSyntaxUtilities.isDigit( ch ) || (currentTokenType == TOKEN_NUMBER && ch == '.')) )
|
||||||
|
newTokenType = TOKEN_NUMBER;
|
||||||
|
else if( ch == ',' || ch == '(' || ch == ')' || ch == '"' || ch == '%' )
|
||||||
|
newTokenType = TokenTypes.OPERATOR;
|
||||||
|
else
|
||||||
|
newTokenType = TOKEN_STRING;
|
||||||
|
|
||||||
|
if( currentTokenType == Token.NULL )
|
||||||
|
currentTokenType = newTokenType;
|
||||||
|
else if( newTokenType != currentTokenType ) {
|
||||||
|
addTokenImpl( array, currentTokenStart, i - 1, currentTokenType, newStartOffset + currentTokenStart );
|
||||||
|
currentTokenType = newTokenType;
|
||||||
|
currentTokenStart = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( currentTokenType != Token.NULL )
|
||||||
|
addTokenImpl( array, currentTokenStart, end, currentTokenType, newStartOffset + currentTokenStart );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTokenImpl( char[] array, int start, int end, int tokenType, int startOffset ) {
|
||||||
|
if( tokenType == TOKEN_STRING ) {
|
||||||
|
int type = tokenMap.get( array, start, end );
|
||||||
|
if( type != -1 )
|
||||||
|
tokenType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// debugOutputToken( array, start, end, tokenType );
|
||||||
|
super.addToken( array, start, end, tokenType, startOffset, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPropertyChar( char ch ) {
|
||||||
|
return RSyntaxUtilities.isLetterOrDigit( ch ) || ch == '.' || ch == '_' || ch == '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
/*debug
|
||||||
|
private java.util.HashMap<Integer, String> tokenTypeStrMap;
|
||||||
|
|
||||||
|
private void debugInputToken( char[] array, int start, int end, int tokenType, int startOffset, boolean hyperlink ) {
|
||||||
|
if( tokenTypeStrMap == null ) {
|
||||||
|
tokenTypeStrMap = new java.util.HashMap<>();
|
||||||
|
for( java.lang.reflect.Field f : TokenTypes.class.getFields() ) {
|
||||||
|
try {
|
||||||
|
tokenTypeStrMap.put( (Integer) f.get( null ), f.getName() );
|
||||||
|
} catch( IllegalArgumentException | IllegalAccessException ex ) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String tokenTypeStr = tokenTypeStrMap.computeIfAbsent( tokenType, t -> {
|
||||||
|
return "(unknown " + t + ")";
|
||||||
|
} );
|
||||||
|
|
||||||
|
System.out.printf( "%d-%d (%d) %-30s '%s'\n",
|
||||||
|
start, end, end - start, tokenTypeStr, new String( array, start, end - start + 1 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void debugOutputToken( char[] array, int start, int end, int tokenType ) {
|
||||||
|
String tokenTypeStr = null;
|
||||||
|
switch( tokenType ) {
|
||||||
|
case TOKEN_PROPERTY: tokenTypeStr = "PROPERTY"; break;
|
||||||
|
case TOKEN_VARIABLE: tokenTypeStr = "VARIABLE"; break;
|
||||||
|
case TOKEN_NUMBER: tokenTypeStr = "NUMBER"; break;
|
||||||
|
case TOKEN_COLOR: tokenTypeStr = "COLOR"; break;
|
||||||
|
case TOKEN_STRING: tokenTypeStr = "STRING"; break;
|
||||||
|
case TOKEN_FUNCTION: tokenTypeStr = "FUNCTION"; break;
|
||||||
|
case TOKEN_TYPE: tokenTypeStr = "TYPE"; break;
|
||||||
|
case TokenTypes.OPERATOR: tokenTypeStr = "OPERATOR"; break;
|
||||||
|
case TokenTypes.WHITESPACE: tokenTypeStr = "WHITESPACE"; break;
|
||||||
|
case TokenTypes.LITERAL_BOOLEAN: tokenTypeStr = "BOOLEAN"; break;
|
||||||
|
case TokenTypes.RESERVED_WORD: tokenTypeStr = "RESERVED_WORD"; break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException( String.valueOf( tokenType ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.printf( " %d-%d (%d) %-15s '%s'\n",
|
||||||
|
start, end, end - start, tokenTypeStr, new String( array, start, end - start + 1 ) );
|
||||||
|
}
|
||||||
|
debug*/
|
||||||
|
}
|
||||||
40
flatlaf-theme-editor/theme-editor-test.properties
Normal file
40
flatlaf-theme-editor/theme-editor-test.properties
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#
|
||||||
|
# some comment
|
||||||
|
#
|
||||||
|
|
||||||
|
ButtonUI=com.formdev.flatlaf.ui.FlatButtonUI
|
||||||
|
CheckBoxUI = com.formdev.flatlaf.ui.FlatCheckBoxUI
|
||||||
|
|
||||||
|
Prop.string=some string
|
||||||
|
Prop.string2="some string 123 #f80"
|
||||||
|
Prop.char1=x
|
||||||
|
Prop.char2=\u2022
|
||||||
|
Prop.integer=123
|
||||||
|
Prop.float=456.123
|
||||||
|
Prop.color1=#f80
|
||||||
|
Prop.color2=#ff8800
|
||||||
|
Prop.color3=#f804
|
||||||
|
Prop.color4=#ff880044
|
||||||
|
Prop.border=1,2,3,4
|
||||||
|
|
||||||
|
Prop.scaledInteger={scaledInteger}123
|
||||||
|
|
||||||
|
Prop.null=null
|
||||||
|
Prop.false=false
|
||||||
|
Prop.true=true
|
||||||
|
|
||||||
|
@var1=123
|
||||||
|
Prop.var=@var1
|
||||||
|
Prop.ref=$Prop.string
|
||||||
|
|
||||||
|
Prop.lazy=lazy(Prop.string)
|
||||||
|
|
||||||
|
Prop.colorFunc1=rgb(12,34,56)
|
||||||
|
Prop.colorFunc2=rgba(12,34,56,78)
|
||||||
|
Prop.colorFunc3=hsl(12,34,56)
|
||||||
|
Prop.colorFunc4=hsla(12,34,56,78)
|
||||||
|
|
||||||
|
Prop.colorFunc5=lighten(#fe1289,20%)
|
||||||
|
Prop.colorFunc6=darken(#fe1289,20%)
|
||||||
|
Prop.colorFunc7=lighten($Prop.colorFunc4,20%,relative autoInverse)
|
||||||
|
Prop.colorFunc8=lighten(Prop.colorFunc4,20%,lazy)
|
||||||
Reference in New Issue
Block a user