Theme Editor:

- support dark theme (menu "View > Dark Laf")
- moved RSyntaxTextArea theme config from XML to properties files
- bracket matching enabled
- highlight selected tab background
This commit is contained in:
Karl Tauber
2021-08-08 17:43:59 +02:00
parent b65db707ed
commit 71ba8f55a7
8 changed files with 314 additions and 109 deletions

View File

@@ -26,6 +26,7 @@ import java.util.Map;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.text.BadLocationException;
import org.fife.ui.rsyntaxtextarea.TextEditorPane;
import org.fife.ui.rsyntaxtextarea.Token;
@@ -47,14 +48,19 @@ class FlatSyntaxTextArea
private final Map<String, Color> parsedColorsMap = new HashMap<>();
FlatSyntaxTextArea() {
// this is necessary because RTextAreaBase.init() always sets foreground to black
setForeground( UIManager.getColor( "TextArea.foreground" ) );
// remove Ctrl+Tab and Ctrl+Shift+Tab focus traversal keys to allow tabbed pane to process them
setFocusTraversalKeys( KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, Collections.emptySet() );
setFocusTraversalKeys( KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, Collections.emptySet() );
// add editor actions
ActionMap actionMap = getActionMap();
actionMap.put( FlatSyntaxTextAreaActions.duplicateLinesUpAction, new DuplicateLinesAction( FlatSyntaxTextAreaActions.duplicateLinesUpAction, true ) );
actionMap.put( FlatSyntaxTextAreaActions.duplicateLinesDownAction, new DuplicateLinesAction( FlatSyntaxTextAreaActions.duplicateLinesDownAction, false ) );
// add editor key strokes
InputMap inputMap = getInputMap();
int defaultModifier = RTextArea.getDefaultModifier();
int alt = InputEvent.ALT_DOWN_MASK;

View File

@@ -22,9 +22,9 @@ import java.awt.Font;
import java.awt.Window;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLayer;
import javax.swing.JOptionPane;
@@ -38,10 +38,13 @@ import org.fife.ui.autocomplete.CompletionProvider;
import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;
import org.fife.ui.rsyntaxtextarea.ErrorStrip;
import org.fife.ui.rsyntaxtextarea.FileLocation;
import org.fife.ui.rsyntaxtextarea.Style;
import org.fife.ui.rsyntaxtextarea.SyntaxScheme;
import org.fife.ui.rsyntaxtextarea.TextEditorPane;
import org.fife.ui.rsyntaxtextarea.Theme;
import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
import org.fife.ui.rsyntaxtextarea.TokenTypes;
import org.fife.ui.rtextarea.Gutter;
import org.fife.ui.rtextarea.RTextArea;
import org.fife.ui.rtextarea.RTextScrollPane;
import com.formdev.flatlaf.util.UIScale;
@@ -60,6 +63,7 @@ class FlatThemeEditorPane
private final CollapsibleSectionPanel collapsiblePanel;
private final RTextScrollPane scrollPane;
private final FlatSyntaxTextArea textArea;
private final ErrorStrip errorStrip;
private FlatFindReplaceBar findReplaceBar;
private File file;
@@ -82,20 +86,6 @@ class FlatThemeEditorPane
firePropertyChange( DIRTY_PROPERTY, e.getOldValue(), e.getNewValue() );
} );
// theme
try( InputStream in = getClass().getResourceAsStream( "light.xml" ) ) {
Theme theme = Theme.load( in );
theme.apply( textArea );
} catch( IOException ex ) {
ex.printStackTrace();
}
// use semitransparent token background because token background
// is painted over mark occurrences background
SyntaxScheme scheme = textArea.getSyntaxScheme();
scheme.getStyle( FlatThemeTokenMaker.TOKEN_COLOR ).background = new Color( 0x0a000000, true );
scheme.getStyle( FlatThemeTokenMaker.TOKEN_VARIABLE ).background = new Color( 0x1800cc00, true );
// autocomplete
CompletionProvider provider = new FlatCompletionProvider();
AutoCompletion ac = new AutoCompletion( provider );
@@ -111,29 +101,49 @@ class FlatThemeEditorPane
// create scroll pane
scrollPane = new RTextScrollPane( overlay );
scrollPane.setBorder( null );
scrollPane.setBorder( BorderFactory.createEmptyBorder() );
scrollPane.setLineNumbersEnabled( true );
// scale fonts
if( UIScale.getUserScaleFactor() != 1 )
textArea.setFont( scaleFont( textArea.getFont() ) );
// use same font for line numbers as in editor
scrollPane.getGutter().setLineNumberFont( textArea.getFont() );
// create error strip
ErrorStrip errorStrip = new ErrorStrip( textArea );
errorStrip = new ErrorStrip( textArea );
// create collapsible panel
collapsiblePanel = new CollapsibleSectionPanel();
collapsiblePanel.add( scrollPane );
collapsiblePanel.add( errorStrip, BorderLayout.LINE_END );
add( collapsiblePanel, BorderLayout.CENTER );
updateTheme();
}
private static Font scaleFont( Font font ) {
int newFontSize = UIScale.scale( font.getSize() );
return font.deriveFont( (float) newFontSize );
void updateTheme() {
Font defaultFont = RTextArea.getDefaultFont();
Font font = defaultFont.deriveFont( (float) UIManager.getFont( "defaultFont" ).getSize() );
textArea.setFont( font );
textArea.setBackground( UIManager.getColor( "FlatThemeEditorPane.background" ) );
textArea.setCaretColor( UIManager.getColor( "FlatThemeEditorPane.caretColor" ) );
textArea.setSelectionColor( UIManager.getColor( "FlatThemeEditorPane.selectionBackground" ) );
textArea.setCurrentLineHighlightColor( UIManager.getColor( "FlatThemeEditorPane.currentLineHighlight" ) );
textArea.setMarkAllHighlightColor( UIManager.getColor( "FlatThemeEditorPane.markAllHighlightColor" ) );
textArea.setMarkOccurrencesColor( UIManager.getColor( "FlatThemeEditorPane.markOccurrencesColor" ) );
textArea.setMatchedBracketBGColor( UIManager.getColor( "FlatThemeEditorPane.matchedBracketBackground" ) );
textArea.setMatchedBracketBorderColor( UIManager.getColor( "FlatThemeEditorPane.matchedBracketBorderColor" ) );
textArea.setPaintMatchedBracketPair( true );
textArea.setAnimateBracketMatching( false );
// syntax
textArea.setSyntaxScheme( new FlatSyntaxScheme( font ) );
// gutter
Gutter gutter = scrollPane.getGutter();
gutter.setBackground( UIManager.getColor( "FlatThemeEditorPane.gutter.background" ) );
gutter.setBorderColor( UIManager.getColor( "FlatThemeEditorPane.gutter.borderColor" ) );
gutter.setLineNumberColor( UIManager.getColor( "FlatThemeEditorPane.gutter.lineNumberColor" ) );
gutter.setLineNumberFont( font );
// error strip
errorStrip.setCaretMarkerColor( UIManager.getColor( "FlatThemeEditorPane.errorstrip.caretMarkerColor" ) );
}
@Override
@@ -233,4 +243,43 @@ class FlatThemeEditorPane
collapsiblePanel.showBottomComponent( findReplaceBar );
}
//---- class FlatSyntaxScheme ---------------------------------------------
private static class FlatSyntaxScheme
extends SyntaxScheme
{
FlatSyntaxScheme( Font baseFont ) {
super( false );
Style[] styles = getStyles();
for( int i = 0; i < styles.length; i++ )
styles[i] = new Style( Color.red );
init( "property", FlatThemeTokenMaker.TOKEN_PROPERTY, baseFont );
init( "variable", FlatThemeTokenMaker.TOKEN_VARIABLE, baseFont );
init( "number", FlatThemeTokenMaker.TOKEN_NUMBER, baseFont );
init( "color", FlatThemeTokenMaker.TOKEN_COLOR, baseFont );
init( "string", FlatThemeTokenMaker.TOKEN_STRING, baseFont );
init( "function", FlatThemeTokenMaker.TOKEN_FUNCTION, baseFont );
init( "type", FlatThemeTokenMaker.TOKEN_TYPE, baseFont );
init( "reservedWord", TokenTypes.RESERVED_WORD, baseFont );
init( "literalBoolean", TokenTypes.LITERAL_BOOLEAN, baseFont );
init( "operator", TokenTypes.OPERATOR, baseFont );
init( "separator", TokenTypes.SEPARATOR, baseFont );
init( "whitespace", TokenTypes.WHITESPACE, baseFont );
init( "comment", TokenTypes.COMMENT_EOL, baseFont );
}
private void init( String key, int token, Font baseFont ) {
String prefix = "FlatThemeEditorPane.style.";
Color fg = UIManager.getColor( prefix + key );
Color bg = UIManager.getColor( prefix + key + ".background" );
boolean italic = UIManager.getBoolean( prefix + key + ".italic" );
Font font = Style.DEFAULT_FONT;
if( italic )
font = baseFont.deriveFont( Font.ITALIC );
getStyles()[token] = new Style( fg, bg, font );
}
}
}

View File

@@ -37,6 +37,8 @@ import java.util.function.Supplier;
import java.util.prefs.Preferences;
import javax.swing.*;
import net.miginfocom.swing.*;
import com.formdev.flatlaf.FlatDarkLaf;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.extras.FlatInspector;
import com.formdev.flatlaf.extras.FlatSVGIcon;
@@ -59,6 +61,7 @@ public class FlatThemeFileEditor
private static final String KEY_RECENT_DIRECTORY = "recentDirectory";
private static final String KEY_RECENT_FILE = "recentFile";
private static final String KEY_WINDOW_BOUNDS = "windowBounds";
private static final String KEY_LAF = "laf";
private File dir;
private Preferences state;
@@ -70,7 +73,15 @@ public class FlatThemeFileEditor
: null;
SwingUtilities.invokeLater( () -> {
FlatLightLaf.setup();
FlatLaf.registerCustomDefaultsSource( "com.formdev.flatlaf.themeeditor" );
try {
String laf = Preferences.userRoot().node( PREFS_ROOT_PATH ).get( KEY_LAF, FlatLightLaf.class.getName() );
UIManager.setLookAndFeel( laf );
} catch( Exception ex ) {
FlatLightLaf.setup();
}
FlatInspector.install( "ctrl alt shift X" );
FlatUIDefaultsInspector.install( "ctrl shift alt Y" );
@@ -83,6 +94,8 @@ public class FlatThemeFileEditor
initComponents();
openDirectoryButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/themeeditor/icons/menu-open.svg" ) );
if( UIManager.getLookAndFeel() instanceof FlatDarkLaf )
darkLafMenuItem.setSelected( true );
restoreState();
restoreWindowBounds();
@@ -338,6 +351,29 @@ public class FlatThemeFileEditor
themeEditorPane.showFindReplaceBar();
}
private void lightLaf() {
applyLookAndFeel( FlatLightLaf.class.getName() );
}
private void darkLaf() {
applyLookAndFeel( FlatDarkLaf.class.getName() );
}
private void applyLookAndFeel( String lafClassName ) {
if( UIManager.getLookAndFeel().getClass().getName().equals( lafClassName ) )
return;
try {
UIManager.setLookAndFeel( lafClassName );
FlatLaf.updateUI();
for( FlatThemeEditorPane themeEditorPane : getThemeEditorPanes() )
themeEditorPane.updateTheme();
state.put( KEY_LAF, lafClassName );
} catch( Exception ex ) {
ex.printStackTrace();
}
}
private void restoreState() {
state = Preferences.userRoot().node( PREFS_ROOT_PATH );
@@ -438,6 +474,9 @@ public class FlatThemeFileEditor
exitMenuItem = new JMenuItem();
editMenu = new JMenu();
findMenuItem = new JMenuItem();
viewMenu = new JMenu();
lightLafMenuItem = new JRadioButtonMenuItem();
darkLafMenuItem = new JRadioButtonMenuItem();
windowMenu = new JMenu();
nextEditorMenuItem = new JMenuItem();
previousEditorMenuItem = new JMenuItem();
@@ -512,6 +551,28 @@ public class FlatThemeFileEditor
}
menuBar.add(editMenu);
//======== viewMenu ========
{
viewMenu.setText("View");
viewMenu.setMnemonic('V');
//---- lightLafMenuItem ----
lightLafMenuItem.setText("Light Laf");
lightLafMenuItem.setMnemonic('L');
lightLafMenuItem.setSelected(true);
lightLafMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0));
lightLafMenuItem.addActionListener(e -> lightLaf());
viewMenu.add(lightLafMenuItem);
//---- darkLafMenuItem ----
darkLafMenuItem.setText("Dark Laf");
darkLafMenuItem.setMnemonic('D');
darkLafMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0));
darkLafMenuItem.addActionListener(e -> darkLaf());
viewMenu.add(darkLafMenuItem);
}
menuBar.add(viewMenu);
//======== windowMenu ========
{
windowMenu.setText("Window");
@@ -570,6 +631,11 @@ public class FlatThemeFileEditor
tabbedPane.addChangeListener(e -> selectedTabChanged());
}
contentPane.add(tabbedPane, BorderLayout.CENTER);
//---- buttonGroup1 ----
ButtonGroup buttonGroup1 = new ButtonGroup();
buttonGroup1.add(lightLafMenuItem);
buttonGroup1.add(darkLafMenuItem);
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
@@ -581,6 +647,9 @@ public class FlatThemeFileEditor
private JMenuItem exitMenuItem;
private JMenu editMenu;
private JMenuItem findMenuItem;
private JMenu viewMenu;
private JRadioButtonMenuItem lightLafMenuItem;
private JRadioButtonMenuItem darkLafMenuItem;
private JMenu windowMenu;
private JMenuItem nextEditorMenuItem;
private JMenuItem previousEditorMenuItem;

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.3.1.342" Java: "15" encoding: "UTF-8"
JFDML JFormDesigner: "7.0.4.0.360" Java: "16" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -95,6 +95,28 @@ new FormModel {
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "find", false ) )
} )
} )
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "viewMenu"
"text": "View"
"mnemonic": 86
add( new FormComponent( "javax.swing.JRadioButtonMenuItem" ) {
name: "lightLafMenuItem"
"text": "Light Laf"
"$buttonGroup": new FormReference( "buttonGroup1" )
"mnemonic": 76
"selected": true
"accelerator": static javax.swing.KeyStroke getKeyStroke( 122, 0, false )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "lightLaf", false ) )
} )
add( new FormComponent( "javax.swing.JRadioButtonMenuItem" ) {
name: "darkLafMenuItem"
"text": "Dark Laf"
"$buttonGroup": new FormReference( "buttonGroup1" )
"mnemonic": 68
"accelerator": static javax.swing.KeyStroke getKeyStroke( 123, 0, false )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "darkLaf", false ) )
} )
} )
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "windowMenu"
"text": "Window"
@@ -119,5 +141,10 @@ new FormModel {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 535, 300 )
} )
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
name: "buttonGroup1"
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 310 )
} )
}
}

View File

@@ -127,8 +127,10 @@ public class FlatThemeTokenMaker
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 == '%' )
else if( ch == ',' || ch == '"' || ch == '%' )
newTokenType = TokenTypes.OPERATOR;
else if( ch == '(' || ch == ')' )
newTokenType = TokenTypes.SEPARATOR; // necessary for bracket matching
else
newTokenType = TOKEN_STRING;