From dadc3351783ea45e1e6f3d9174d3c2cfda7b0e33 Mon Sep 17 00:00:00 2001 From: hneemann Date: Mon, 22 Jun 2020 18:24:20 +0200 Subject: [PATCH] first implementation of different color schemes, see #421, see #481 --- .../de/neemann/digital/core/element/Key.java | 18 +++ .../de/neemann/digital/core/element/Keys.java | 2 +- .../digital/draw/graphics/ColorMap.java | 153 ++++++++++++++++++ .../neemann/digital/draw/graphics/Style.java | 37 +++-- .../digital/draw/shapes/SevenShape.java | 5 +- .../digital/draw/shapes/SixteenShape.java | 5 +- .../java/de/neemann/digital/gui/Main.java | 2 +- .../java/de/neemann/digital/gui/Settings.java | 16 ++ .../gui/components/CircuitComponent.java | 7 +- src/main/resources/lang/lang_de.xml | 5 + src/main/resources/lang/lang_en.xml | 5 + 11 files changed, 225 insertions(+), 30 deletions(-) create mode 100644 src/main/java/de/neemann/digital/draw/graphics/ColorMap.java diff --git a/src/main/java/de/neemann/digital/core/element/Key.java b/src/main/java/de/neemann/digital/core/element/Key.java index 8fedb7762..008095485 100644 --- a/src/main/java/de/neemann/digital/core/element/Key.java +++ b/src/main/java/de/neemann/digital/core/element/Key.java @@ -23,6 +23,7 @@ public class Key { private CheckEnabled checkEnabled; private boolean isSecondary; private boolean requiresRestart = false; + private boolean requiresRepaint = false; private String panelId; // Both values are always null in digital. @@ -235,6 +236,23 @@ public class Key { return requiresRestart; } + /** + * Called if this setting needs a repaint. + * + * @return this for chained calls + */ + public Key setRequiresRepaint() { + requiresRepaint = true; + return this; + } + + /** + * @return true if changing this value needs a repaint + */ + public boolean getRequiresRepaint() { + return requiresRepaint; + } + /** * Moves this key to the panel with the given id * diff --git a/src/main/java/de/neemann/digital/core/element/Keys.java b/src/main/java/de/neemann/digital/core/element/Keys.java index 02bccd6da..9cd8ce5b9 100644 --- a/src/main/java/de/neemann/digital/core/element/Keys.java +++ b/src/main/java/de/neemann/digital/core/element/Keys.java @@ -466,7 +466,7 @@ public final class Keys { * enables the grid */ public static final Key SETTINGS_GRID - = new Key<>("grid", true); + = new Key<>("grid", true).setRequiresRepaint(); /** * enables the wire bits view diff --git a/src/main/java/de/neemann/digital/draw/graphics/ColorMap.java b/src/main/java/de/neemann/digital/draw/graphics/ColorMap.java new file mode 100644 index 000000000..6a3c82ba0 --- /dev/null +++ b/src/main/java/de/neemann/digital/draw/graphics/ColorMap.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2020 Helmut Neemann. + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ +package de.neemann.digital.draw.graphics; + +import de.neemann.digital.core.element.Key; +import de.neemann.digital.gui.Settings; + +import java.awt.*; + +/** + * Color map. + * Used to define the different color schemes. + */ +public final class ColorMap { + + private static final ColorMap DEFAULT_MAP = new ColorMap() + .set(ColorKey.BACKGROUND, Color.WHITE) + .set(ColorKey.MAIN, Color.BLACK) + .set(ColorKey.WIRE, Color.BLUE.darker()) + .set(ColorKey.WIRE_LOW, new Color(0, 142, 0)) + .set(ColorKey.WIRE_HIGH, new Color(102, 255, 102)) + .set(ColorKey.WIRE_OUT, Color.RED.darker()) + .set(ColorKey.WIRE_VALUE, new Color(50, 162, 50)) + .set(ColorKey.WIRE_Z, Color.GRAY) + .set(ColorKey.PINS, Color.GRAY) + .set(ColorKey.HIGHLIGHT, Color.CYAN) + .set(ColorKey.GRID, new Color(210, 210, 210)) + .set(ColorKey.PASSED, Color.GREEN) + .set(ColorKey.ERROR, Color.RED); + + private static final ColorMap DARK_MAP = new ColorMap() + .set(ColorKey.BACKGROUND, Color.BLACK) + .set(ColorKey.MAIN, Color.GRAY) + .set(ColorKey.WIRE, Color.BLUE.darker()) + .set(ColorKey.WIRE_LOW, new Color(0, 142, 0)) + .set(ColorKey.WIRE_HIGH, new Color(102, 255, 102)) + .set(ColorKey.WIRE_OUT, Color.RED.darker()) + .set(ColorKey.WIRE_VALUE, new Color(50, 162, 50)) + .set(ColorKey.WIRE_Z, Color.GRAY) + .set(ColorKey.PINS, Color.GRAY) + .set(ColorKey.HIGHLIGHT, Color.CYAN) + .set(ColorKey.GRID, new Color(50, 50, 50)) + .set(ColorKey.PASSED, Color.GREEN) + .set(ColorKey.ERROR, Color.RED); + + private static final ColorMap COLOR_BLIND_MAP = new ColorMap() + .set(ColorKey.BACKGROUND, Color.WHITE) + .set(ColorKey.MAIN, Color.BLACK) + .set(ColorKey.WIRE, Color.BLUE.darker()) + .set(ColorKey.WIRE_LOW, new Color(32, 59, 232)) + .set(ColorKey.WIRE_HIGH, new Color(244, 235, 66)) + .set(ColorKey.WIRE_OUT, Color.RED.darker()) + .set(ColorKey.WIRE_VALUE, new Color(50, 162, 50)) + .set(ColorKey.WIRE_Z, new Color(1, 188, 157)) + .set(ColorKey.PINS, Color.GRAY) + .set(ColorKey.HIGHLIGHT, Color.CYAN) + .set(ColorKey.GRID, new Color(210, 210, 210)) + .set(ColorKey.PASSED, Color.GREEN) + .set(ColorKey.ERROR, Color.RED); + + private enum ColorSchemes { + DEFAULT(DEFAULT_MAP), DARK(DARK_MAP), COLOR_BLIND(COLOR_BLIND_MAP); + + private final ColorMap map; + + ColorSchemes(ColorMap map) { + this.map = map; + } + + private ColorMap getMap() { + return map; + } + } + + /** + * The key used to select the color map + */ + public static final Key COLOR_SCHEME = + new Key.KeyEnum<>("colorScheme", ColorSchemes.DEFAULT, ColorSchemes.values()) + .setRequiresRepaint(); + + private static ColorMap instance = null; + + /** + * @return the selected color map + */ + public static ColorMap getInstance() { + if (instance == null) { + Settings.getInstance().getAttributes().addListener(ColorMap::updateInstance); + updateInstance(); + } + return instance; + } + + private static void updateInstance() { + instance = Settings.getInstance().get(COLOR_SCHEME).getMap(); + } + + /** + * The identifiers for the different colors + */ + public enum ColorKey {BACKGROUND, MAIN, WIRE, WIRE_HIGH, WIRE_LOW, WIRE_VALUE, WIRE_OUT, WIRE_Z, ERROR, PASSED, PINS, GRID, HIGHLIGHT} + + private final Color[] colors; + + private ColorMap() { + colors = new Color[ColorKey.HIGHLIGHT.ordinal() + 1]; + } + + private ColorMap set(ColorKey key, Color color) { + colors[key.ordinal()] = color; + return this; + } + + /** + * Returns the selected color + * + * @param key te color key + * @return the color + */ + public Color getColor(ColorKey key) { + Color color = colors[key.ordinal()]; + if (color == null) + return colors[ColorKey.MAIN.ordinal()]; + return color; + } + + /** + * Provides a color + */ + public interface ColorProvider { + /** + * @return the color + */ + Color getColor(); + } + + static final class ColorByKey implements ColorProvider { + private final ColorKey key; + + ColorByKey(ColorKey key) { + this.key = key; + } + + @Override + public Color getColor() { + return getInstance().getColor(key); + } + } +} diff --git a/src/main/java/de/neemann/digital/draw/graphics/Style.java b/src/main/java/de/neemann/digital/draw/graphics/Style.java index 2cbdb2c0d..c970fd3d7 100644 --- a/src/main/java/de/neemann/digital/draw/graphics/Style.java +++ b/src/main/java/de/neemann/digital/draw/graphics/Style.java @@ -37,11 +37,11 @@ public final class Style { /** * used to draw the failed state lines in the measurement graph */ - public static final Style FAILED = new Builder(NORMAL).setColor(Color.RED).build(); + public static final Style FAILED = new Builder(NORMAL).setColor(ColorMap.ColorKey.ERROR).build(); /** * used to draw the passed state lines in the measurement graph */ - public static final Style PASS = new Builder(NORMAL).setColor(Color.GREEN).build(); + public static final Style PASS = new Builder(NORMAL).setColor(ColorMap.ColorKey.PASSED).build(); /** * Used for text which is integral part of the shape. * Text which uses this style is always included in sizing! @@ -66,25 +66,25 @@ public final class Style { public static final Style WIRE = new Builder() .setThickness(WIRETHICK) .setFilled(true) - .setColor(Color.BLUE.darker()) + .setColor(ColorMap.ColorKey.WIRE) .setEndCap(BasicStroke.CAP_ROUND) .build(); /** * Used for low wires in running mode */ - public static final Style WIRE_LOW = new Builder(WIRE).setColor(new Color(0, 142, 0)).build(); + public static final Style WIRE_LOW = new Builder(WIRE).setColor(ColorMap.ColorKey.WIRE_LOW).build(); /** * Used for high wires in running mode */ - public static final Style WIRE_HIGH = new Builder(WIRE).setColor(new Color(102, 255, 102)).build(); + public static final Style WIRE_HIGH = new Builder(WIRE).setColor(ColorMap.ColorKey.WIRE_HIGH).build(); /** * Used for wires in high Z state */ - public static final Style WIRE_HIGHZ = new Builder(WIRE).setColor(Color.GRAY).build(); + public static final Style WIRE_HIGHZ = new Builder(WIRE).setColor(ColorMap.ColorKey.WIRE_Z).build(); /** * used to draw the output dots */ - public static final Style WIRE_OUT = new Builder(WIRE).setColor(Color.RED.darker()).build(); + public static final Style WIRE_OUT = new Builder(WIRE).setColor(ColorMap.ColorKey.WIRE_OUT).build(); /** * used to draw the bus wires @@ -107,7 +107,7 @@ public final class Style { */ public static final Style SHAPE_PIN = new Builder() .setThickness(LINETHIN) - .setColor(Color.GRAY) + .setColor(ColorMap.ColorKey.PINS) .setFontSize(18) .build(); /** @@ -118,19 +118,19 @@ public final class Style { * Used to draw the pin description text */ public static final Style WIRE_VALUE = new Builder(SHAPE_SPLITTER) - .setColor(new Color(50, 162, 50)) + .setColor(ColorMap.ColorKey.WIRE_VALUE) .build(); /** * Used to draw the wire bit number */ public static final Style WIRE_BITS = new Builder(SHAPE_SPLITTER) - .setColor(WIRE.color) + .setColor(ColorMap.ColorKey.WIRE) .build(); /** * highlight color used for the circles to mark an element */ public static final Style HIGHLIGHT = new Builder(NORMAL) - .setColor(Color.CYAN) + .setColor(ColorMap.ColorKey.HIGHLIGHT) .setEndCap(BasicStroke.CAP_ROUND) .build(); @@ -138,13 +138,13 @@ public final class Style { * error color used for the circles to mark an element */ public static final Style ERROR = new Builder(NORMAL) - .setColor(Color.RED) + .setColor(ColorMap.ColorKey.ERROR) .setEndCap(BasicStroke.CAP_ROUND) .build(); private final int thickness; private final boolean filled; - private final Color color; + private final ColorMap.ColorProvider color; private final int fontSize; private final float[] dash; private final BasicStroke stroke; @@ -188,7 +188,7 @@ public final class Style { * @return the color */ public Color getColor() { - return color; + return color.getColor(); } /** @@ -309,7 +309,7 @@ public final class Style { private static final class Builder { private int thickness = LINETHICK; private boolean filled = false; - private Color color = Color.BLACK; + private ColorMap.ColorProvider color = new ColorMap.ColorByKey(ColorMap.ColorKey.MAIN); private int fontSize = 24; private float[] dash = null; private boolean mattersForSize = false; @@ -339,8 +339,13 @@ public final class Style { return this; } + private Builder setColor(ColorMap.ColorKey key) { + this.color = new ColorMap.ColorByKey(key); + return this; + } + private Builder setColor(Color color) { - this.color = color; + this.color = () -> color; return this; } diff --git a/src/main/java/de/neemann/digital/draw/shapes/SevenShape.java b/src/main/java/de/neemann/digital/draw/shapes/SevenShape.java index 60af25bea..b364e9b04 100644 --- a/src/main/java/de/neemann/digital/draw/shapes/SevenShape.java +++ b/src/main/java/de/neemann/digital/draw/shapes/SevenShape.java @@ -8,9 +8,6 @@ package de.neemann.digital.draw.shapes; import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.core.element.Keys; import de.neemann.digital.draw.graphics.*; -import de.neemann.digital.draw.graphics.Polygon; - -import java.awt.*; /** * The shape to show a seven seg display. @@ -46,7 +43,7 @@ public abstract class SevenShape implements Shape { */ public SevenShape(ElementAttributes attr) { onStyle = Style.NORMAL.deriveFillStyle(attr.get(Keys.COLOR)); - offStyle = Style.NORMAL.deriveFillStyle(new Color(230, 230, 230)); + offStyle = Style.NORMAL.deriveFillStyle(ColorMap.getInstance().getColor(ColorMap.ColorKey.GRID)); size = attr.get(Keys.SEVEN_SEG_SIZE); } diff --git a/src/main/java/de/neemann/digital/draw/shapes/SixteenShape.java b/src/main/java/de/neemann/digital/draw/shapes/SixteenShape.java index 4c4181b59..33bd37e8d 100644 --- a/src/main/java/de/neemann/digital/draw/shapes/SixteenShape.java +++ b/src/main/java/de/neemann/digital/draw/shapes/SixteenShape.java @@ -14,9 +14,6 @@ import de.neemann.digital.draw.elements.IOState; import de.neemann.digital.draw.elements.Pin; import de.neemann.digital.draw.elements.Pins; import de.neemann.digital.draw.graphics.*; -import de.neemann.digital.draw.graphics.Polygon; - -import java.awt.*; import static de.neemann.digital.draw.shapes.GenericShape.SIZE; @@ -67,7 +64,7 @@ public class SixteenShape implements Shape { public SixteenShape(ElementAttributes attr, PinDescriptions inputs, PinDescriptions outputs) { pins = inputs; onStyle = Style.NORMAL.deriveFillStyle(attr.get(Keys.COLOR)); - offStyle = Style.NORMAL.deriveFillStyle(new Color(230, 230, 230)); + offStyle = Style.NORMAL.deriveFillStyle(ColorMap.getInstance().getColor(ColorMap.ColorKey.GRID)); size = attr.get(Keys.SEVEN_SEG_SIZE); } diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java index e758856b8..fb0053567 100644 --- a/src/main/java/de/neemann/digital/gui/Main.java +++ b/src/main/java/de/neemann/digital/gui/Main.java @@ -745,7 +745,7 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS Lang.setLanguage(modified.get(Keys.SETTINGS_LANGUAGE)); JOptionPane.showMessageDialog(Main.this, Lang.get("msg_restartNeeded")); } - if (!Settings.getInstance().getAttributes().equalsKey(Keys.SETTINGS_GRID, modified)) + if (Settings.getInstance().requiresRepaint(modified)) circuitComponent.graphicHasChanged(); Settings.getInstance().getAttributes().getValuesFrom(modified); diff --git a/src/main/java/de/neemann/digital/gui/Settings.java b/src/main/java/de/neemann/digital/gui/Settings.java index b51eab3f3..e2386d9b4 100644 --- a/src/main/java/de/neemann/digital/gui/Settings.java +++ b/src/main/java/de/neemann/digital/gui/Settings.java @@ -8,6 +8,7 @@ package de.neemann.digital.gui; import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.core.element.Key; import de.neemann.digital.core.element.Keys; +import de.neemann.digital.draw.graphics.ColorMap; import java.util.ArrayList; import java.util.Collections; @@ -42,6 +43,7 @@ public final class Settings extends SettingsBase { intList.add(Keys.SETTINGS_IEEE_SHAPES); intList.add(Keys.SETTINGS_LANGUAGE); intList.add(Keys.SETTINGS_EXPRESSION_FORMAT); + intList.add(ColorMap.COLOR_SCHEME); intList.add(Keys.SETTINGS_DEFAULT_TREESELECT); intList.add(Keys.SETTINGS_GRID); intList.add(Keys.SETTINGS_SHOW_WIRE_BITS); @@ -76,4 +78,18 @@ public final class Settings extends SettingsBase { return false; } + /** + * Returns true if the given modification requires a repaint. + * + * @param modified the modified settings + * @return true if the modification requires a repaint + */ + public boolean requiresRepaint(ElementAttributes modified) { + for (Key key : getKeys()) + if (key.getRequiresRepaint() && !getAttributes().equalsKey(key, modified)) + return true; + + return false; + } + } diff --git a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java index 2e771d674..8a0418a3b 100644 --- a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java +++ b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java @@ -43,6 +43,7 @@ import java.io.IOException; import java.util.List; import java.util.*; +import static de.neemann.digital.draw.graphics.ColorMap.*; import static de.neemann.digital.draw.shapes.GenericShape.SIZE; import static de.neemann.digital.draw.shapes.GenericShape.SIZE2; @@ -92,8 +93,6 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib private static final int DRAG_DISTANCE = (int) (SIZE2 * Screen.getInstance().getScaling()); - private static final Color GRID_COLOR = new Color(210, 210, 210); - private final Main parent; private final ElementLibrary library; private final HashSet highLighted; @@ -859,7 +858,7 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib Graphics2D gr2 = buffer.createGraphics(); enableAntiAlias(gr2); - gr2.setColor(Color.WHITE); + gr2.setColor(getInstance().getColor(ColorKey.BACKGROUND)); gr2.fillRect(0, 0, getWidth(), getHeight()); if (scaleX > 0.3 && Settings.getInstance().get(Keys.SETTINGS_GRID)) @@ -917,7 +916,7 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib if (delta > max) delta = max; double sub = delta / 2.0; - gr2.setColor(GRID_COLOR); + gr2.setColor(getInstance().getColor(ColorKey.GRID)); for (int x = 0; x <= cx; x++) { double xx = p1.getX() + (p2.getX() - p1.getX()) * x / cx - sub; for (int y = 0; y <= cy; y++) { diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index 050aec39a..dd26bdffa 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -1506,6 +1506,11 @@ Sind evtl. die Namen der Variablen nicht eindeutig? Wird vom Layout-Shape verwendet. Legt den Abstand zum vorherigen Pin fest. + Farbschema + Normal + Dunkel + Rot-Grün-Sehschwäche + Leitung eingefügt. Aus Zwischenablage eingefügt. Wert ''{0}'' in Element ''{1}'' verändert. diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index 98fd0dc27..5d55d23d9 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -1470,6 +1470,11 @@ Used by the layout shape type. Sets the distance to the previous pin. + Color-Scheme + Normal + Dark + red/green colorblind + Inserted wire. Insert from clipboard. Value ''{0}'' in component ''{1}'' modified.