diff --git a/flatlaf-theme-editor/build.gradle.kts b/flatlaf-theme-editor/build.gradle.kts index 969d0d08..e759f118 100644 --- a/flatlaf-theme-editor/build.gradle.kts +++ b/flatlaf-theme-editor/build.gradle.kts @@ -18,10 +18,18 @@ plugins { `java-library` } +repositories { + maven { + // for using MigLayout snapshot + url = uri( "https://oss.sonatype.org/content/repositories/snapshots/" ) + } +} + dependencies { implementation( project( ":flatlaf-core" ) ) implementation( project( ":flatlaf-extras" ) ) + implementation( "com.miglayout:miglayout-swing:5.3-SNAPSHOT" ) implementation( "com.fifesoft:rsyntaxtextarea:3.1.1" ) implementation( "com.fifesoft:autocomplete:3.1.0" ) } diff --git a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeEditorPane.java b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeEditorPane.java index 7e2afde3..f8384290 100644 --- a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeEditorPane.java +++ b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeEditorPane.java @@ -31,6 +31,7 @@ import org.fife.ui.autocomplete.CompletionProvider; import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory; import org.fife.ui.rsyntaxtextarea.FileLocation; 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.rtextarea.RTextScrollPane; @@ -44,6 +45,8 @@ import com.formdev.flatlaf.util.UIScale; class FlatThemeEditorPane extends JPanel { + static final String DIRTY_PROPERTY = TextEditorPane.DIRTY_PROPERTY; + private static final String FLATLAF_STYLE = "text/flatlaf"; private final RTextScrollPane scrollPane; @@ -63,6 +66,10 @@ class FlatThemeEditorPane textArea.addParser( new FlatThemeParser() ); // textArea.setUseColorOfColorTokens( true ); + textArea.addPropertyChangeListener( TextEditorPane.DIRTY_PROPERTY, e -> { + firePropertyChange( DIRTY_PROPERTY, e.getOldValue(), e.getNewValue() ); + } ); + // theme try( InputStream in = getClass().getResourceAsStream( "light.xml" ) ) { Theme theme = Theme.load( in ); @@ -117,11 +124,15 @@ class FlatThemeEditorPane textArea.load( loc, StandardCharsets.ISO_8859_1 ); } - void save() { - try { - textArea.save(); - } catch( IOException ex ) { - ex.printStackTrace(); // TODO - } + void save() throws IOException { + textArea.save(); + } + + String getFileName() { + return textArea.getFileName(); + } + + boolean isDirty() { + return textArea.isDirty(); } } diff --git a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeFileEditor.java b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeFileEditor.java index 49ddd1de..aa585931 100644 --- a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeFileEditor.java +++ b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeFileEditor.java @@ -20,16 +20,19 @@ import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; import java.awt.Toolkit; -import java.awt.event.KeyEvent; +import java.awt.event.*; import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import java.util.Arrays; +import java.util.function.Supplier; import javax.swing.*; +import com.formdev.flatlaf.extras.components.*; +import net.miginfocom.swing.*; import org.fife.ui.rsyntaxtextarea.FileLocation; import com.formdev.flatlaf.FlatLightLaf; import com.formdev.flatlaf.extras.FlatInspector; import com.formdev.flatlaf.extras.FlatUIDefaultsInspector; +import com.formdev.flatlaf.util.StringUtils; import com.formdev.flatlaf.util.UIScale; /** @@ -41,13 +44,9 @@ public class FlatThemeFileEditor extends JFrame { public static void main( String[] args ) { - File file = new File( args.length > 0 + File dir = new File( args.length > 0 ? args[0] - : "theme-editor-test.properties" ); // TODO - - List baseFiles = new ArrayList<>(); - for( int i = 1; i < args.length; i++ ) - baseFiles.add( new File( args[i] ) ); + : "." ); SwingUtilities.invokeLater( () -> { FlatLightLaf.install(); @@ -56,18 +55,7 @@ public class FlatThemeFileEditor FlatThemeFileEditor frame = new FlatThemeFileEditor(); - frame.themeEditorArea.setBaseFiles( baseFiles ); - try { - frame.themeEditorArea.load( FileLocation.create( file ) ); - } catch( IOException ex ) { - ex.printStackTrace(); - } - - int menuShortcutKeyMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); - ((JComponent)frame.getContentPane()).registerKeyboardAction( - e -> frame.themeEditorArea.save(), - KeyStroke.getKeyStroke( KeyEvent.VK_S, menuShortcutKeyMask ), - JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ); + frame.loadDirectory( dir ); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); frame.setSize( Math.min( UIScale.scale( 800 ), screenSize.width ), @@ -81,28 +69,169 @@ public class FlatThemeFileEditor initComponents(); } + public void loadDirectory( File dir ) { + try { + directoryField.setText( dir.getCanonicalPath() ); + } catch( IOException ex ) { + directoryField.setText( dir.getAbsolutePath() ); + } + + File[] propertiesFiles = dir.listFiles( (d, name) -> { + return name.endsWith( ".properties" ); + } ); + + Arrays.sort( propertiesFiles ); + + for( File file : propertiesFiles ) + openFile( file ); + } + + public void openFile( File file ) { + FlatThemeEditorPane themeEditorArea = new FlatThemeEditorPane(); + try { + themeEditorArea.load( FileLocation.create( file ) ); + } catch( IOException ex ) { + ex.printStackTrace(); // TODO + } + + Supplier titleFun = () -> { + return (themeEditorArea.isDirty() ? "*" : "") + + StringUtils.removeTrailing( themeEditorArea.getFileName(), ".properties" ); + + }; + themeEditorArea.addPropertyChangeListener( FlatThemeEditorPane.DIRTY_PROPERTY, e -> { + int index = tabbedPane.indexOfComponent( themeEditorArea ); + if( index >= 0 ) + tabbedPane.setTitleAt( index, titleFun.get() ); + } ); + + tabbedPane.addTab( titleFun.get(), null, themeEditorArea, file.getAbsolutePath() ); + } + + private boolean saveAll() { + for( FlatThemeEditorPane themeEditorPane : getThemeEditorPanes() ) { + try { + if( themeEditorPane.isDirty() ) + themeEditorPane.save(); + } catch( IOException ex ) { + JOptionPane.showMessageDialog( this, + "Failed to save '" + themeEditorPane.getFileName() + "'\n\nReason: " + ex.getMessage(), + getTitle(), JOptionPane.WARNING_MESSAGE ); + return false; + } + } + return true; + } + + private void exit() { + if( saveAll() ) + System.exit( 0 ); + } + + private void windowClosing() { + exit(); + } + + private void windowDeactivated() { + saveAll(); + } + + private FlatThemeEditorPane[] getThemeEditorPanes() { + FlatThemeEditorPane[] result = new FlatThemeEditorPane[tabbedPane.getTabCount()]; + for( int i = 0; i < result.length; i++ ) + result[i] = (FlatThemeEditorPane) tabbedPane.getComponentAt( i ); + return result; + } + private void initComponents() { // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents - dialogPane = new JPanel(); - themeEditorArea = new FlatThemeEditorPane(); + menuBar = new JMenuBar(); + fileMenu = new JMenu(); + saveAllMenuItem = new JMenuItem(); + exitMenuItem = new JMenuItem(); + controlPanel = new JPanel(); + directoryLabel = new JLabel(); + directoryField = new JTextField(); + tabbedPane = new FlatTabbedPane(); //======== this ======== - setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); setTitle("FlatLaf Theme Editor"); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + FlatThemeFileEditor.this.windowClosing(); + } + @Override + public void windowDeactivated(WindowEvent e) { + FlatThemeFileEditor.this.windowDeactivated(); + } + }); Container contentPane = getContentPane(); contentPane.setLayout(new BorderLayout()); - //======== dialogPane ======== + //======== menuBar ======== { - dialogPane.setLayout(new BorderLayout()); - dialogPane.add(themeEditorArea, BorderLayout.CENTER); + + //======== fileMenu ======== + { + fileMenu.setText("File"); + + //---- saveAllMenuItem ---- + saveAllMenuItem.setText("Save All"); + saveAllMenuItem.setMnemonic('S'); + saveAllMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + saveAllMenuItem.addActionListener(e -> saveAll()); + fileMenu.add(saveAllMenuItem); + fileMenu.addSeparator(); + + //---- exitMenuItem ---- + exitMenuItem.setText("Exit"); + exitMenuItem.setMnemonic('X'); + exitMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + exitMenuItem.addActionListener(e -> exit()); + fileMenu.add(exitMenuItem); + } + menuBar.add(fileMenu); } - contentPane.add(dialogPane, BorderLayout.CENTER); + setJMenuBar(menuBar); + + //======== controlPanel ======== + { + controlPanel.setLayout(new MigLayout( + "hidemode 3", + // columns + "[fill]" + + "[grow,fill]", + // rows + "[]")); + + //---- directoryLabel ---- + directoryLabel.setText("Directory:"); + controlPanel.add(directoryLabel, "cell 0 0"); + + //---- directoryField ---- + directoryField.setEditable(false); + controlPanel.add(directoryField, "cell 1 0"); + } + contentPane.add(controlPanel, BorderLayout.NORTH); + + //======== tabbedPane ======== + { + tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + } + contentPane.add(tabbedPane, BorderLayout.CENTER); // JFormDesigner - End of component initialization //GEN-END:initComponents } // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables - private JPanel dialogPane; - private FlatThemeEditorPane themeEditorArea; + private JMenuBar menuBar; + private JMenu fileMenu; + private JMenuItem saveAllMenuItem; + private JMenuItem exitMenuItem; + private JPanel controlPanel; + private JLabel directoryLabel; + private JTextField directoryField; + private FlatTabbedPane tabbedPane; // JFormDesigner - End of variables declaration //GEN-END:variables } diff --git a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeFileEditor.jfd b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeFileEditor.jfd index 7b56ce1d..fe8bd768 100644 --- a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeFileEditor.jfd +++ b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeFileEditor.jfd @@ -1,4 +1,4 @@ -JFDML JFormDesigner: "7.0.2.0.298" Java: "14" encoding: "UTF-8" +JFDML JFormDesigner: "7.0.3.1.342" Java: "15" encoding: "UTF-8" new FormModel { contentType: "form/swing" @@ -7,18 +7,61 @@ new FormModel { name: "this" "$locationPolicy": 2 "$sizePolicy": 2 - "defaultCloseOperation": 3 + "defaultCloseOperation": 0 "title": "FlatLaf Theme Editor" - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) { - name: "dialogPane" - add( new FormComponent( "com.formdev.flatlaf.themeeditor.FlatThemeEditorPane" ) { - name: "themeEditorArea" - }, new FormLayoutConstraints( class java.lang.String ) { - "value": "Center" + addEvent( new FormEvent( "java.awt.event.WindowListener", "windowClosing", "windowClosing", false ) ) + addEvent( new FormEvent( "java.awt.event.WindowListener", "windowDeactivated", "windowDeactivated", false ) ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "hidemode 3" + "$columnConstraints": "[fill][grow,fill]" + "$rowConstraints": "[]" + } ) { + name: "controlPanel" + add( new FormComponent( "javax.swing.JLabel" ) { + name: "directoryLabel" + "text": "Directory:" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" } ) + add( new FormComponent( "javax.swing.JTextField" ) { + name: "directoryField" + "editable": false + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 0" + } ) + }, new FormLayoutConstraints( class java.lang.String ) { + "value": "North" + } ) + add( new FormContainer( "com.formdev.flatlaf.extras.components.FlatTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) { + name: "tabbedPane" + "tabLayoutPolicy": 1 }, new FormLayoutConstraints( class java.lang.String ) { "value": "Center" } ) + menuBar: new FormContainer( "javax.swing.JMenuBar", new FormLayoutManager( class javax.swing.JMenuBar ) ) { + name: "menuBar" + add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) { + name: "fileMenu" + "text": "File" + add( new FormComponent( "javax.swing.JMenuItem" ) { + name: "saveAllMenuItem" + "text": "Save All" + "mnemonic": 83 + "accelerator": static javax.swing.KeyStroke getKeyStroke( 83, 4226, false ) + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "saveAll", false ) ) + } ) + add( new FormComponent( "javax.swing.JPopupMenu$Separator" ) { + name: "separator1" + } ) + add( new FormComponent( "javax.swing.JMenuItem" ) { + name: "exitMenuItem" + "text": "Exit" + "mnemonic": 88 + "accelerator": static javax.swing.KeyStroke getKeyStroke( 81, 4226, false ) + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "exit", false ) ) + } ) + } ) + } }, new FormLayoutConstraints( null ) { "location": new java.awt.Point( 0, 0 ) "size": new java.awt.Dimension( 535, 300 )