diff --git a/.gitignore b/.gitignore index 6df9964..a10ca8d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ target/ .mvn mvnw* -docs/.obsidian \ No newline at end of file +docs/.obsidian +*.jar diff --git a/pom.xml b/pom.xml index acbda56..45fbed0 100644 --- a/pom.xml +++ b/pom.xml @@ -84,6 +84,12 @@ main-SNAPSHOT + + com.github.Glimmr-Lang + PiccodePlugin + main-SNAPSHOT + + diff --git a/src/main/java/org/editor/CanvasFrame.java b/src/main/java/org/editor/CanvasFrame.java index 594f720..a4a3886 100644 --- a/src/main/java/org/editor/CanvasFrame.java +++ b/src/main/java/org/editor/CanvasFrame.java @@ -27,6 +27,7 @@ import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.plaf.basic.BasicGraphicsUtils; +import org.editor.theme.ThemeManager; import org.piccode.ast.Ast; import org.piccode.backend.Compiler; import org.piccode.rt.Context; @@ -254,12 +255,12 @@ private void drawGrid() { gridImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = gridImage.createGraphics(); // Fill background - g2.setColor(Color.WHITE); + g2.setColor(ThemeManager.RENDER_BG); g2.fillRect(0, 0, getWidth(), getHeight()); if (showGrid) { // Draw grid - g2.setColor(new Color(230, 230, 230)); + g2.setColor(ThemeManager.RENDER_GRID); for (int x = -offsetX % SCALE; x < getWidth(); x += SCALE) { g2.drawLine(x, 0, x, getHeight()); } @@ -270,7 +271,7 @@ private void drawGrid() { if (showRuler) { // Draw axis numbers - g2.setColor(Color.GRAY); + g2.setColor(ThemeManager.RENDER_GRID); for (int x = -offsetX % SCALE; x < getWidth(); x += SCALE) { int value = (x + offsetX) / SCALE; g2.drawString(Integer.toString(value * SCALE), x + 2, 12); @@ -286,7 +287,7 @@ private void drawGrid() { g2.setColor(Color.RED); g2.drawString("y", 8, getHeight() - 5); - g2.setColor(Color.GRAY); + g2.setColor(ThemeManager.RENDER_TXT2); int x = (-offsetX % SCALE + offsetX) / SCALE; int y = (-offsetY % SCALE + offsetY) / SCALE; x *= SCALE; diff --git a/src/main/java/org/editor/CodeEditor.java b/src/main/java/org/editor/CodeEditor.java index d55e319..e37cdc7 100644 --- a/src/main/java/org/editor/CodeEditor.java +++ b/src/main/java/org/editor/CodeEditor.java @@ -30,6 +30,7 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaHighlighter; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.fife.ui.rsyntaxtextarea.TextEditorPane; +import org.fife.ui.rsyntaxtextarea.Theme; import org.fife.ui.rsyntaxtextarea.TokenMakerFactory; import org.fife.ui.rsyntaxtextarea.templates.CodeTemplate; import org.fife.ui.rsyntaxtextarea.templates.StaticCodeTemplate; @@ -297,4 +298,15 @@ public DockKey getDockKey() { public Component getComponent() { return this; } + + public void setThemeMode(boolean dark) { + var themeName = dark ? "monokai" : "vs"; + try { + Theme theme = Theme.load(getClass().getResourceAsStream( + "/org/fife/ui/rsyntaxtextarea/themes/" + themeName +".xml")); + theme.apply(textArea); + } catch (IOException ioe) { // Never happens + ioe.printStackTrace(); + } + } } diff --git a/src/main/java/org/editor/EditorWindow.java b/src/main/java/org/editor/EditorWindow.java index be507e1..b43054c 100644 --- a/src/main/java/org/editor/EditorWindow.java +++ b/src/main/java/org/editor/EditorWindow.java @@ -43,6 +43,7 @@ import org.editor.panels.FileTreePanel; import org.editor.panels.PluginsPanel; import org.editor.panels.VCPanel; +import org.editor.theme.ThemeManager; import org.fife.rsta.ui.CollapsibleSectionPanel; //import org.fife.rsta.ui.DocumentMap; @@ -72,8 +73,9 @@ public final class EditorWindow extends JFrame implements SearchListener { private CollapsibleSectionPanel csp; public static FindDialog findDialog; public static ReplaceDialog replaceDialog; - private DockingDesktop desk = new DockingDesktop(); + public static DockingDesktop desk = new DockingDesktop(); private static CodeEditor selected = null; + public static boolean dark = true; public static EditorWindow the() { if (win == null) { @@ -87,17 +89,13 @@ public static EditorWindow the() { public EditorWindow() { super("Piccode - DashBoard"); + ThemeManager.setFlatLaf(dark); DockingUISettings.getInstance().installUI(); customizeDock(); UIManager.put("Tree.collapsedIcon", UIManager.getIcon("Tree.collapsedIcon")); UIManager.put("Tree.expandedIcon", UIManager.getIcon("Tree.expandedIcon")); - try { - UIManager.setLookAndFeel(new FlatLightLaf()); - } catch (Exception ex) { - System.err.println("Failed to initialize LaF"); - } new CodeEditor(); root = getRootPane(); @@ -118,7 +116,7 @@ public EditorWindow() { if (current.getDockable() instanceof CodeEditor ed) { if (event.getFutureState().isClosed()) { - if (removeIfDirty(ed.tabIndex, ed) == false) { + if (removeIfNotDirty(ed.tabIndex, ed) == false) { event.cancel(); } } @@ -229,6 +227,8 @@ public EditorWindow() { win = this; + ThemeManager.updateThemes(dark); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(width, height); this.setLocationRelativeTo(null); @@ -251,6 +251,8 @@ public static void addTab(ActionEvent e) { editor.requestFocusInWindow(); current_file.setText(file != null ? file.toString() : "[NONE]"); tabEditors.put(index, editor); + ThemeManager.registerEditor(editor); + ThemeManager.updateThemes(dark); // Add first editor normally if (index == 0) { @@ -271,6 +273,8 @@ public static void addTab(Path path, Void e) { editor.requestFocusInWindow(); tabEditors.put(index, editor); + ThemeManager.registerEditor(editor); + ThemeManager.updateThemes(dark); // Add first editor normally if (index == 0) { win.getContentPane().add(editor); @@ -339,7 +343,7 @@ private static Component makeTabHeader(JTabbedPane tabs, String title) { int index = tabs.indexOfTabComponent(tabHeader); if (index != -1) { var ed = tabEditors.get(index); - removeIfDirty(index, ed); + removeIfNotDirty(index, ed); } }); @@ -364,13 +368,13 @@ public static void removeTab() { return; } - removeIfDirty(index, focused); + removeIfNotDirty(index, focused); } public static void removeAllTabs() { var editors = new HashMap<>(tabEditors); // Copy to avoid ConcurrentModificationException for (var entry : editors.entrySet()) { - removeIfDirty(entry.getKey(), entry.getValue()); + removeIfNotDirty(entry.getKey(), entry.getValue()); } } @@ -378,7 +382,7 @@ public static int tabsCount() { return tabEditors.size(); } - private static boolean removeIfDirty(Integer index, CodeEditor ed) { + private static boolean removeIfNotDirty(Integer index, CodeEditor ed) { if (ed.textArea.isDirty()) { int result = JOptionPane.showConfirmDialog(win, "File " + ed.filePathTruncated() + " is modified. Save?"); if (result == JOptionPane.OK_OPTION) { @@ -387,6 +391,7 @@ private static boolean removeIfDirty(Integer index, CodeEditor ed) { } win.desk.remove((Dockable) ed); // Actual removal from docking layout tabEditors.remove(index); + ThemeManager.removeEditor(ed); migrateIndexes(); return true; diff --git a/src/main/java/org/editor/dialogs/EditorSettingsDialog.form b/src/main/java/org/editor/dialogs/EditorSettingsDialog.form new file mode 100644 index 0000000..c619847 --- /dev/null +++ b/src/main/java/org/editor/dialogs/EditorSettingsDialog.form @@ -0,0 +1,224 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/org/editor/dialogs/EditorSettingsDialog.java b/src/main/java/org/editor/dialogs/EditorSettingsDialog.java new file mode 100644 index 0000000..1e4098a --- /dev/null +++ b/src/main/java/org/editor/dialogs/EditorSettingsDialog.java @@ -0,0 +1,181 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/GUIForms/JPanel.java to edit this template + */ +package org.editor.dialogs; + +/** + * + * @author hexaredecimal + */ +public class EditorSettingsDialog extends javax.swing.JPanel { + + /** + * Creates new form EditorSettingsDialog + */ + public EditorSettingsDialog() { + initComponents(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jPanel1 = new javax.swing.JPanel(); + jLabel1 = new javax.swing.JLabel(); + jTextField1 = new javax.swing.JTextField(); + jButton1 = new javax.swing.JButton(); + jLabel2 = new javax.swing.JLabel(); + jSpinner1 = new javax.swing.JSpinner(); + jPanel2 = new javax.swing.JPanel(); + jCheckBox1 = new javax.swing.JCheckBox(); + jCheckBox2 = new javax.swing.JCheckBox(); + jCheckBox3 = new javax.swing.JCheckBox(); + jPanel3 = new javax.swing.JPanel(); + jLabel3 = new javax.swing.JLabel(); + jComboBox1 = new javax.swing.JComboBox<>(); + + jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder("Font")); + + jLabel1.setText("Font"); + + jButton1.setText("Pick Font"); + + jLabel2.setText("Font Size"); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(jLabel2, javax.swing.GroupLayout.DEFAULT_SIZE, 92, Short.MAX_VALUE) + .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 226, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jButton1) + .addGap(0, 0, Short.MAX_VALUE)) + .addComponent(jSpinner1)) + .addContainerGap()) + ); + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel1) + .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jButton1)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel2) + .addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder("General")); + + jCheckBox1.setText("Show Line Numbers"); + + jCheckBox2.setText("Enable Syntax Highlighter"); + + jCheckBox3.setText("Enable BookMarks"); + + javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); + jPanel2.setLayout(jPanel2Layout); + jPanel2Layout.setHorizontalGroup( + jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jCheckBox2) + .addComponent(jCheckBox1) + .addComponent(jCheckBox3)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + jPanel2Layout.setVerticalGroup( + jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addComponent(jCheckBox2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jCheckBox1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jCheckBox3) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + jPanel3.setBorder(javax.swing.BorderFactory.createTitledBorder("LookAndFeel")); + + jLabel3.setText("Dark/Light Mode"); + + jComboBox1.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Dark Mode", "Light Mode" })); + + javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3); + jPanel3.setLayout(jPanel3Layout); + jPanel3Layout.setHorizontalGroup( + jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel3Layout.createSequentialGroup() + .addContainerGap() + .addComponent(jLabel3, javax.swing.GroupLayout.PREFERRED_SIZE, 157, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(18, 18, 18) + .addComponent(jComboBox1, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap()) + ); + jPanel3Layout.setVerticalGroup( + jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel3Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel3)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton jButton1; + private javax.swing.JCheckBox jCheckBox1; + private javax.swing.JCheckBox jCheckBox2; + private javax.swing.JCheckBox jCheckBox3; + private javax.swing.JComboBox jComboBox1; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; + private javax.swing.JPanel jPanel1; + private javax.swing.JPanel jPanel2; + private javax.swing.JPanel jPanel3; + private javax.swing.JSpinner jSpinner1; + private javax.swing.JTextField jTextField1; + // End of variables declaration//GEN-END:variables +} diff --git a/src/main/java/org/editor/dialogs/GeneralSettingsDialog.form b/src/main/java/org/editor/dialogs/GeneralSettingsDialog.form new file mode 100644 index 0000000..b450678 --- /dev/null +++ b/src/main/java/org/editor/dialogs/GeneralSettingsDialog.form @@ -0,0 +1,197 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/editor/dialogs/GeneralSettingsDialog.java b/src/main/java/org/editor/dialogs/GeneralSettingsDialog.java new file mode 100644 index 0000000..2883926 --- /dev/null +++ b/src/main/java/org/editor/dialogs/GeneralSettingsDialog.java @@ -0,0 +1,166 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/GUIForms/JPanel.java to edit this template + */ +package org.editor.dialogs; + +/** + * + * @author hexaredecimal + */ +public class GeneralSettingsDialog extends javax.swing.JPanel { + + /** + * Creates new form GeneralSettingsDialog + */ + public GeneralSettingsDialog() { + initComponents(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jPanel1 = new javax.swing.JPanel(); + jLabel1 = new javax.swing.JLabel(); + jTextField1 = new javax.swing.JTextField(); + jLabel2 = new javax.swing.JLabel(); + jTextField2 = new javax.swing.JTextField(); + jButton1 = new javax.swing.JButton(); + jPanel2 = new javax.swing.JPanel(); + jLabel3 = new javax.swing.JLabel(); + jTextField3 = new javax.swing.JTextField(); + jLabel4 = new javax.swing.JLabel(); + jTextField4 = new javax.swing.JTextField(); + jLabel5 = new javax.swing.JLabel(); + + jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder("General")); + + jLabel1.setText("Working Directory"); + + jLabel2.setText("Project Name"); + + jButton1.setLabel("Update"); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 150, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jTextField1)) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 150, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jTextField2, javax.swing.GroupLayout.DEFAULT_SIZE, 349, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(jButton1))) + .addContainerGap()) + ); + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel1) + .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel2) + .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 15, Short.MAX_VALUE) + .addComponent(jButton1) + .addContainerGap()) + ); + + jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder("Runtime")); + + jLabel3.setText("Entry File"); + + jTextField3.setText("./main.pics"); + + jLabel4.setText("Arguments"); + + jLabel5.setFont(new java.awt.Font("sansserif", 0, 10)); // NOI18N + jLabel5.setForeground(new java.awt.Color(204, 204, 204)); + jLabel5.setText("NOTE: Arguments are separated by a space"); + + javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); + jPanel2.setLayout(jPanel2Layout); + jPanel2Layout.setHorizontalGroup( + jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(jLabel4, javax.swing.GroupLayout.DEFAULT_SIZE, 94, Short.MAX_VALUE) + .addComponent(jLabel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jTextField3) + .addComponent(jTextField4, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(jLabel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) + ); + jPanel2Layout.setVerticalGroup( + jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel3) + .addComponent(jTextField3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel4) + .addComponent(jTextField4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel5) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jPanel1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton jButton1; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; + private javax.swing.JLabel jLabel4; + private javax.swing.JLabel jLabel5; + private javax.swing.JPanel jPanel1; + private javax.swing.JPanel jPanel2; + private javax.swing.JTextField jTextField1; + private javax.swing.JTextField jTextField2; + private javax.swing.JTextField jTextField3; + private javax.swing.JTextField jTextField4; + // End of variables declaration//GEN-END:variables +} diff --git a/src/main/java/org/editor/dialogs/SettingsDialog.form b/src/main/java/org/editor/dialogs/SettingsDialog.form new file mode 100644 index 0000000..a459d89 --- /dev/null +++ b/src/main/java/org/editor/dialogs/SettingsDialog.form @@ -0,0 +1,79 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/org/editor/dialogs/SettingsDialog.java b/src/main/java/org/editor/dialogs/SettingsDialog.java new file mode 100644 index 0000000..072d6ec --- /dev/null +++ b/src/main/java/org/editor/dialogs/SettingsDialog.java @@ -0,0 +1,82 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/GUIForms/JPanel.java to edit this template + */ +package org.editor.dialogs; + +/** + * + * @author hexaredecimal + */ +public class SettingsDialog extends javax.swing.JPanel { + + /** + * Creates new form SettingsDialog + */ + public SettingsDialog() { + initComponents(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jPanel1 = new javax.swing.JPanel(); + jTabbedPane1 = new javax.swing.JTabbedPane(); + jButton1 = new javax.swing.JButton(); + jButton2 = new javax.swing.JButton(); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jTabbedPane1) + ); + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(jTabbedPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 303, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE)) + ); + + jButton1.setText("jButton1"); + + jButton2.setText("jButton2"); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap(333, Short.MAX_VALUE) + .addComponent(jButton2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jButton1) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jButton1) + .addComponent(jButton2)) + .addGap(0, 13, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton jButton1; + private javax.swing.JButton jButton2; + private javax.swing.JPanel jPanel1; + private javax.swing.JTabbedPane jTabbedPane1; + // End of variables declaration//GEN-END:variables +} diff --git a/src/main/java/org/editor/panels/PluginsPanel.java b/src/main/java/org/editor/panels/PluginsPanel.java index 0013be2..3a38918 100644 --- a/src/main/java/org/editor/panels/PluginsPanel.java +++ b/src/main/java/org/editor/panels/PluginsPanel.java @@ -1,6 +1,18 @@ package org.editor.panels; +import com.piccode.piccodeplugin.PiccodePluginPanel; import java.awt.BorderLayout; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.jar.JarFile; +import org.editor.EditorWindow; +import org.editor.theme.ThemeManager; /** * @@ -8,10 +20,83 @@ */ public class PluginsPanel extends DockablePanel { + private final HashMap loaders = new HashMap<>(); public PluginsPanel() { super(new BorderLayout(), "Plugins", "Plugins", "Browse community plugins", "plugin"); - // TODO: Implement the code + loadPlugins(); + } + + private void loadPlugins() { + var pluginsDir = Paths.get("./etc/plugins/"); + try { + Class baseClass = Class.forName("com.piccode.piccodeplugin.PiccodePluginPanel"); + Files.walk(pluginsDir) + .filter(p -> p.toString().endsWith(".jar")) + .forEach(jarPath -> { + try { + scanJar(jarPath.toFile(), baseClass); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } catch (IOException | ClassNotFoundException ex) { + System.out.println(ex.getMessage()); + } + } + + private void loadPlugin(Class clazz) { + System.out.println("Loading plugin: " + clazz.getName()); + try { + var instance = (PiccodePluginPanel) clazz.getDeclaredConstructor().newInstance(); + instance.init(); + instance = instance.getMainPanel(); + var name = instance.getPluginName(); + var title = "Plugin: " + name; + var dockable = new DockablePanel(new BorderLayout(), name, title, instance.getDescription(), "plugin"); + dockable.add(instance, BorderLayout.CENTER); + ThemeManager.registerPlugin(instance); + var desk = EditorWindow.desk; + desk.addDockable(dockable); + desk.setAutoHide(dockable, true); + ThemeManager.updateThemes(EditorWindow.dark); + + } catch (Exception ex) { + System.out.println("" + ex.getMessage()); + } + } + + private void scanJar(File jarFile, Class baseClass) throws IOException { + URL[] urls = {jarFile.toURI().toURL()}; + // Use system classloader as parent to share core and app classes + URLClassLoader cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader()); + loaders.put(jarFile, cl); // keep the loader alive + + try (JarFile jar = new JarFile(jarFile)) { + jar.stream() + .filter(e -> e.getName().endsWith(".class") + && !e.getName().equals("module-info.class") + && !e.getName().contains("$") + && !e.getName().contains("META-INF/") + && !e.getName().endsWith("package-info.class")) + .forEach(e -> { + String className = e.getName().replace('/', '.').replaceAll("\\.class$", ""); + try { + // Try loading from app classloader first (fallback pattern) + Class clazz; + try { + clazz = Class.forName(className); + } catch (ClassNotFoundException ex) { + clazz = cl.loadClass(className); + } + if (baseClass.isAssignableFrom(clazz) && !clazz.equals(baseClass)) { + loadPlugin(clazz); + } + } catch (Exception ignore) { + System.out.println("Failed to load class " + className + ": " + ignore.getMessage()); + } + }); + } } } diff --git a/src/main/java/org/editor/theme/ThemeManager.java b/src/main/java/org/editor/theme/ThemeManager.java new file mode 100644 index 0000000..d3a6287 --- /dev/null +++ b/src/main/java/org/editor/theme/ThemeManager.java @@ -0,0 +1,67 @@ +package org.editor.theme; + +import com.formdev.flatlaf.FlatDarkLaf; +import com.formdev.flatlaf.FlatLightLaf; +import com.piccode.piccodeplugin.PiccodePluginInterface; +import java.awt.Color; +import java.util.ArrayList; +import java.util.List; +import org.editor.CodeEditor; + +/** + * + * @author hexaredecimal + */ +public class ThemeManager { + + private static List editors = new ArrayList(); + private static List plugins = new ArrayList(); + public static Color RENDER_BG = Color.WHITE; + public static Color RENDER_FG = Color.BLACK; + public static Color RENDER_TXT2 = Color.BLUE; + public static Color RENDER_GRID = Color.GRAY; + + public static void registerEditor(CodeEditor editor) { + editors.add(editor); + } + + public static void removeEditor(CodeEditor editor) { + editors.remove(editor); + } + + public static void registerPlugin(PiccodePluginInterface plugin) { + plugins.add(plugin); + } + + public static void updateThemes(boolean dark) { + editors.forEach(editor -> editor.setThemeMode(dark)); + plugins.forEach(plugin -> plugin.setThemeMode(dark)); + setFlatLaf(dark); + + if (dark) { + RENDER_BG = new Color(18, 18, 18); + RENDER_FG = Color.WHITE; + RENDER_TXT2 = Color.GREEN; + RENDER_GRID = new Color(18 * 5, 18 * 5, 18 * 5); + } else { + RENDER_BG = Color.WHITE; + RENDER_FG = Color.BLACK; + RENDER_TXT2 = Color.BLUE; + RENDER_GRID = new Color(230, 230, 230); + } + } + + public static void setFlatLaf(boolean dark) { + try { + if (dark) { + FlatDarkLaf.setup(); + FlatDarkLaf.updateUI(); + } else { + FlatLightLaf.setup(); + FlatLightLaf.updateUI(); + } + } catch (Exception ex) { + System.err.println("Failed to initialize LaF"); + } + } +}