diff --git a/pom.xml b/pom.xml
index 5ab53c3..ac85a67 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,5 +1,6 @@
-
+
4.0.0
com.loomcom.symon
symon
@@ -15,12 +16,33 @@
UTF-8
+
+
+ jitpack.io
+ https://jitpack.io
+
+
ch.qos.logback
logback-classic
1.5.16
+
+ commons-cli
+ commons-cli
+ 1.5.0
+
+
+ com.formdev
+ flatlaf
+ 3.7.1
+
+
+ com.github.Dansoftowner
+ jSystemThemeDetector
+ 3.9.1
+
junit
junit
@@ -33,11 +55,6 @@
4.11.0
test
-
- commons-cli
- commons-cli
- 1.5.0
-
@@ -48,7 +65,8 @@
3.2.0
-
+
com.loomcom.symon.Main
@@ -120,4 +138,4 @@
-
+
\ No newline at end of file
diff --git a/src/main/java/com/loomcom/symon/Main.java b/src/main/java/com/loomcom/symon/Main.java
index df45c0d..1f3cc88 100644
--- a/src/main/java/com/loomcom/symon/Main.java
+++ b/src/main/java/com/loomcom/symon/Main.java
@@ -25,14 +25,15 @@
package com.loomcom.symon;
import com.loomcom.symon.machines.*;
+import com.loomcom.symon.ui.Ui;
+
import org.apache.commons.cli.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Locale;
-import javax.swing.JOptionPane;
+
import javax.swing.SwingUtilities;
-import javax.swing.UIManager;
public class Main {
private final static Logger logger = LoggerFactory.getLogger(Main.class.getName());
@@ -58,6 +59,7 @@ public class Main {
CommandLineParser parser = new DefaultParser();
try {
+
CommandLine line = parser.parse(options, args);
InstructionTable.CpuBehavior cpuBehavior = null;
String romFile = null;
@@ -116,27 +118,11 @@ public class Main {
haltOnBreak = true;
}
+ Ui.initUi();
+
while (true) {
if (machineClass == null) {
- Object[] possibilities = { "Symon", "Multicomp", "Simple", "BenEater" };
- String s = (String) JOptionPane.showInputDialog(
- null,
- "Please choose the machine type to be emulated:",
- "Machine selection",
- JOptionPane.PLAIN_MESSAGE,
- null,
- possibilities,
- "Symon");
-
- if (s != null && s.equals("Multicomp")) {
- machineClass = MulticompMachine.class;
- } else if (s != null && s.equals("Simple")) {
- machineClass = SimpleMachine.class;
- } else if (s != null && s.equals("BenEater")) {
- machineClass = BenEaterMachine.class;
- } else {
- machineClass = SymonMachine.class;
- }
+ machineClass = Ui.showMachineSelectionDialog();
}
if (cpuBehavior == null) {
@@ -146,9 +132,8 @@ public class Main {
final Simulator simulator = new Simulator(machineClass, cpuBehavior, romFile, haltOnBreak);
SwingUtilities.invokeLater(() -> {
+ // Create the main UI window:
try {
- UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
- // Create the main UI window
simulator.createAndShowUi();
} catch (Exception e) {
logger.error("Error during Symon UI initialization: {}", e.getMessage());
@@ -168,4 +153,5 @@ public class Main {
logger.error("Could not start Symon. Reason: {}", ex.getMessage());
}
}
+
}
diff --git a/src/main/java/com/loomcom/symon/ui/BreakpointsWindow.java b/src/main/java/com/loomcom/symon/ui/BreakpointsWindow.java
index 3794220..581b372 100644
--- a/src/main/java/com/loomcom/symon/ui/BreakpointsWindow.java
+++ b/src/main/java/com/loomcom/symon/ui/BreakpointsWindow.java
@@ -70,7 +70,6 @@ public class BreakpointsWindow extends JFrame {
final JTable breakpointsTable = new JTable(breakpoints);
breakpointsTable.setShowGrid(true);
- breakpointsTable.setGridColor(Color.LIGHT_GRAY);
breakpointsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
breakpointsTable.getSelectionModel().addListSelectionListener(e -> removeButton.setEnabled(e.getFirstIndex() > -1));
diff --git a/src/main/java/com/loomcom/symon/ui/StatusPanel.java b/src/main/java/com/loomcom/symon/ui/StatusPanel.java
index cef25fe..b8878b6 100644
--- a/src/main/java/com/loomcom/symon/ui/StatusPanel.java
+++ b/src/main/java/com/loomcom/symon/ui/StatusPanel.java
@@ -86,7 +86,7 @@ public class StatusPanel extends JPanel {
private void createUi() {
Border emptyBorder = BorderFactory.createEmptyBorder(EMPTY_BORDER, EMPTY_BORDER,
- EMPTY_BORDER, EMPTY_BORDER);
+ EMPTY_BORDER, EMPTY_BORDER);
Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
setBorder(BorderFactory.createCompoundBorder(emptyBorder, etchedBorder));
@@ -125,16 +125,15 @@ public class StatusPanel extends JPanel {
// Add tool-tip text
carryFlagLabel.setToolTipText("Carry: The last operation caused an overflow " +
- "from bit 7 of the result or an underflow from bit 0");
+ "from bit 7 of the result or an underflow from bit 0");
zeroFlagLabel.setToolTipText("Zero: The result of the last operation was 0");
irqDisableFlagLabel.setToolTipText("Interrupt Disable: Processor will not respond to IRQ");
decimalModeFlagLabel.setToolTipText("Decimal Mode");
breakFlagLabel.setToolTipText("Break: BRK instruction occurred");
overflowFlagLabel.setToolTipText("Overflow: The result of the last operation was " +
- "an invalid 2's complement result");
+ "an invalid 2's complement result");
negativeFlagLabel.setToolTipText("Negative: The result of the last operation set bit 7");
-
statusFlagsPanel.add(negativeFlagLabel);
statusFlagsPanel.add(overflowFlagLabel);
statusFlagsPanel.add(breakFlagLabel);
@@ -243,7 +242,6 @@ public class StatusPanel extends JPanel {
constraints.gridy = 1;
add(statusFlagsPanel, constraints);
-
constraints.insets = new Insets(5, 0, 0, 0);
constraints.gridy = 2;
add(opcodeLabel, constraints);
@@ -392,13 +390,6 @@ public class StatusPanel extends JPanel {
textField.setMinimumSize(size);
textField.setMaximumSize(size);
textField.setPreferredSize(size);
- textField.setBackground(Color.WHITE);
- // Although we usually defer to the system look-and-feel, for
- // these small text fields in particular, we use a Metal
- // look-and-feel because native look-and-feel breaks very small
- // text fields under GTK+ (they are drawn with an inner margin
- // even if the margin is set to 0)
- textField.setUI(new MetalTextFieldUI());
return textField;
}
diff --git a/src/main/java/com/loomcom/symon/ui/Ui.java b/src/main/java/com/loomcom/symon/ui/Ui.java
new file mode 100644
index 0000000..57ff169
--- /dev/null
+++ b/src/main/java/com/loomcom/symon/ui/Ui.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2008-2025 Seth J. Morabito
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package com.loomcom.symon.ui;
+
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.formdev.flatlaf.FlatDarkLaf;
+import com.formdev.flatlaf.FlatLaf;
+import com.formdev.flatlaf.FlatLightLaf;
+import com.formdev.flatlaf.util.SystemInfo;
+import com.jthemedetecor.OsThemeDetector;
+import com.loomcom.symon.machines.BenEaterMachine;
+import com.loomcom.symon.machines.MulticompMachine;
+import com.loomcom.symon.machines.SimpleMachine;
+import com.loomcom.symon.machines.SymonMachine;
+
+public final class Ui {
+ private final static Logger logger = LoggerFactory.getLogger(Ui.class.getName());
+
+ public static void initUi() throws UnsupportedLookAndFeelException {
+ // Location of properties resources for app-specific UI defaults:
+ FlatLaf.registerCustomDefaultsSource("com.loomcom.symon.themes");
+
+ // Enable automatic scaling, based on system DPI settings:
+ System.setProperty("sun.java2d.uiScale.enabled", "true");
+ System.setProperty("sun.java2d.uiScale", "1x");
+ System.setProperty("flatlaf.uiScale", "1x");
+
+ // OS-specific tweaking:
+
+ if (SystemInfo.isLinux) {
+ logger.debug("Adapting UI for Linux.");
+
+ // Enable custom window decorations:
+ JFrame.setDefaultLookAndFeelDecorated(true);
+ JDialog.setDefaultLookAndFeelDecorated(true);
+ }
+
+ else if (SystemInfo.isMacOS) {
+ logger.debug("Adapting UI for MacOS.");
+
+ // Move menu bar from JFrame window to top of screen:
+ System.setProperty("apple.laf.useScreenMenuBar", "true");
+ // Set name used in screen menu bar (first menu after Apple menu):
+ System.setProperty("apple.awt.application.name", "Symon");
+ // Use current macOS appearance (light/dark) for window title bars:
+ System.setProperty("apple.awt.application.appearance", "system");
+ }
+
+ else if (SystemInfo.isWindows) {
+ logger.debug("Adapting UI for Windows.");
+ }
+
+ UIManager.setLookAndFeel(createLookAndFeel());
+ }
+
+ public static Class> showMachineSelectionDialog() {
+ Object[] possibilities = { "Symon", "Multicomp", "Simple", "BenEater" };
+ String s = (String) JOptionPane.showInputDialog(
+ null,
+ "Please choose the machine type to be emulated:",
+ "Machine selection",
+ JOptionPane.PLAIN_MESSAGE,
+ null,
+ possibilities,
+ "Symon");
+
+ if (s != null && s.equals("Multicomp")) {
+ return MulticompMachine.class;
+ } else if (s != null && s.equals("Simple")) {
+ return SimpleMachine.class;
+ } else if (s != null && s.equals("BenEater")) {
+ return BenEaterMachine.class;
+ } else {
+ return SymonMachine.class;
+ }
+ }
+
+ private static LookAndFeel createLookAndFeel() {
+ final OsThemeDetector themeDetector = OsThemeDetector.getDetector();
+ final boolean systemThemeIsDark = themeDetector.isDark();
+
+ // Set light/dark mode depending on what can be detected from the system theme:
+ FlatLaf theme;
+ if (systemThemeIsDark) {
+ logger.debug("Using DARK theme.");
+ theme = new FlatDarkLaf();
+ } else {
+ logger.debug("Using LIGHT theme.");
+ theme = new FlatLightLaf();
+ }
+ return theme;
+ }
+
+}
diff --git a/src/main/resources/B_off.png b/src/main/resources/B_off.png
index 145264f..15a9b9d 100644
Binary files a/src/main/resources/B_off.png and b/src/main/resources/B_off.png differ
diff --git a/src/main/resources/C_off.png b/src/main/resources/C_off.png
index 84986cf..8edc212 100644
Binary files a/src/main/resources/C_off.png and b/src/main/resources/C_off.png differ
diff --git a/src/main/resources/D_off.png b/src/main/resources/D_off.png
index 39db75d..a41d035 100644
Binary files a/src/main/resources/D_off.png and b/src/main/resources/D_off.png differ
diff --git a/src/main/resources/I_off.png b/src/main/resources/I_off.png
index 288a487..99ec0bf 100644
Binary files a/src/main/resources/I_off.png and b/src/main/resources/I_off.png differ
diff --git a/src/main/resources/N_off.png b/src/main/resources/N_off.png
index 6468771..2fd8e66 100644
Binary files a/src/main/resources/N_off.png and b/src/main/resources/N_off.png differ
diff --git a/src/main/resources/O_off.png b/src/main/resources/O_off.png
index 51945fb..443e74d 100644
Binary files a/src/main/resources/O_off.png and b/src/main/resources/O_off.png differ
diff --git a/src/main/resources/Z_off.png b/src/main/resources/Z_off.png
index 124dc55..0f12a57 100644
Binary files a/src/main/resources/Z_off.png and b/src/main/resources/Z_off.png differ
diff --git a/src/main/resources/com.loomcom.symon.themes/FlatDarkLaf.properties b/src/main/resources/com.loomcom.symon.themes/FlatDarkLaf.properties
new file mode 100644
index 0000000..0286809
--- /dev/null
+++ b/src/main/resources/com.loomcom.symon.themes/FlatDarkLaf.properties
@@ -0,0 +1,3 @@
+# Loaded for FlatDarkLaf theme only.
+
+Colors.accent.default = #4B6EAF
diff --git a/src/main/resources/com.loomcom.symon.themes/FlatLaf.properties b/src/main/resources/com.loomcom.symon.themes/FlatLaf.properties
new file mode 100644
index 0000000..79bea72
--- /dev/null
+++ b/src/main/resources/com.loomcom.symon.themes/FlatLaf.properties
@@ -0,0 +1 @@
+# Loaded for all FlatLaf themes.
diff --git a/src/main/resources/com.loomcom.symon.themes/FlatLightLaf.properties b/src/main/resources/com.loomcom.symon.themes/FlatLightLaf.properties
new file mode 100644
index 0000000..69cd3c6
--- /dev/null
+++ b/src/main/resources/com.loomcom.symon.themes/FlatLightLaf.properties
@@ -0,0 +1,3 @@
+# Loaded for FlatLightLaf theme only.
+
+Colors.accent.default = #2675BF