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");
+ }
+ }
+}