refactoring of color schemes, see #421, see #481

This commit is contained in:
hneemann 2020-06-23 08:23:51 +02:00
parent dadc335178
commit c59e72e6b4
14 changed files with 204 additions and 189 deletions

View File

@ -0,0 +1,11 @@
/*
* 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;
/**
* 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, TESTCASE, DISABLED, ASYNC, HIGHLIGHT}

View File

@ -1,153 +0,0 @@
/*
* 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<ColorSchemes> 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);
}
}
}

View File

@ -0,0 +1,110 @@
/*
* 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 ColorScheme {
private static final ColorScheme DEFAULT_SCHEME = new ColorScheme()
.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)
.set(ColorKey.DISABLED, Color.LIGHT_GRAY)
.set(ColorKey.TESTCASE, new Color(180, 255, 180, 200))
.set(ColorKey.ASYNC, new Color(255, 180, 180, 200));
private static final ColorScheme DARK_SCHEME = new ColorScheme(DEFAULT_SCHEME)
.set(ColorKey.BACKGROUND, Color.BLACK)
.set(ColorKey.MAIN, Color.GRAY)
.set(ColorKey.GRID, new Color(50, 50, 50))
.set(ColorKey.DISABLED, Color.GRAY.darker().darker());
private static final ColorScheme COLOR_BLIND_SCHEME = new ColorScheme(DEFAULT_SCHEME)
.set(ColorKey.WIRE_LOW, new Color(32, 59, 232))
.set(ColorKey.WIRE_HIGH, new Color(244, 235, 66))
.set(ColorKey.WIRE_Z, new Color(1, 188, 157));
enum ColorSchemes {
DEFAULT(DEFAULT_SCHEME), DARK(DARK_SCHEME), COLOR_BLIND(COLOR_BLIND_SCHEME);
private final ColorScheme scheme;
ColorSchemes(ColorScheme scheme) {
this.scheme = scheme;
}
ColorScheme getScheme() {
return scheme;
}
}
/**
* The key used to select the color map
*/
public static final Key<ColorSchemes> COLOR_SCHEME =
new Key.KeyEnum<>("colorScheme", ColorSchemes.DEFAULT, ColorSchemes.values())
.setRequiresRepaint();
private static ColorScheme instance = null;
/**
* @return the selected color map
*/
public static ColorScheme getInstance() {
if (instance == null) {
updateInstance();
Settings.getInstance().getAttributes().addListener(ColorScheme::updateInstance);
}
return instance;
}
private static void updateInstance() {
instance = Settings.getInstance().get(COLOR_SCHEME).getScheme();
}
private final Color[] colors;
private ColorScheme() {
colors = new Color[ColorKey.values().length];
}
private ColorScheme(ColorScheme other) {
this.colors = other.colors.clone();
}
private ColorScheme 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) {
return colors[key.ordinal()];
}
}

View File

@ -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(ColorMap.ColorKey.ERROR).build();
public static final Style FAILED = new Builder(NORMAL).setColor(ColorKey.ERROR).build();
/**
* used to draw the passed state lines in the measurement graph
*/
public static final Style PASS = new Builder(NORMAL).setColor(ColorMap.ColorKey.PASSED).build();
public static final Style PASS = new Builder(NORMAL).setColor(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(ColorMap.ColorKey.WIRE)
.setColor(ColorKey.WIRE)
.setEndCap(BasicStroke.CAP_ROUND)
.build();
/**
* Used for low wires in running mode
*/
public static final Style WIRE_LOW = new Builder(WIRE).setColor(ColorMap.ColorKey.WIRE_LOW).build();
public static final Style WIRE_LOW = new Builder(WIRE).setColor(ColorKey.WIRE_LOW).build();
/**
* Used for high wires in running mode
*/
public static final Style WIRE_HIGH = new Builder(WIRE).setColor(ColorMap.ColorKey.WIRE_HIGH).build();
public static final Style WIRE_HIGH = new Builder(WIRE).setColor(ColorKey.WIRE_HIGH).build();
/**
* Used for wires in high Z state
*/
public static final Style WIRE_HIGHZ = new Builder(WIRE).setColor(ColorMap.ColorKey.WIRE_Z).build();
public static final Style WIRE_HIGHZ = new Builder(WIRE).setColor(ColorKey.WIRE_Z).build();
/**
* used to draw the output dots
*/
public static final Style WIRE_OUT = new Builder(WIRE).setColor(ColorMap.ColorKey.WIRE_OUT).build();
public static final Style WIRE_OUT = new Builder(WIRE).setColor(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(ColorMap.ColorKey.PINS)
.setColor(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(ColorMap.ColorKey.WIRE_VALUE)
.setColor(ColorKey.WIRE_VALUE)
.build();
/**
* Used to draw the wire bit number
*/
public static final Style WIRE_BITS = new Builder(SHAPE_SPLITTER)
.setColor(ColorMap.ColorKey.WIRE)
.setColor(ColorKey.WIRE)
.build();
/**
* highlight color used for the circles to mark an element
*/
public static final Style HIGHLIGHT = new Builder(NORMAL)
.setColor(ColorMap.ColorKey.HIGHLIGHT)
.setColor(ColorKey.HIGHLIGHT)
.setEndCap(BasicStroke.CAP_ROUND)
.build();
@ -138,13 +138,14 @@ public final class Style {
* error color used for the circles to mark an element
*/
public static final Style ERROR = new Builder(NORMAL)
.setColor(ColorMap.ColorKey.ERROR)
.setColor(ColorKey.ERROR)
.setEndCap(BasicStroke.CAP_ROUND)
.build();
private final int thickness;
private final boolean filled;
private final ColorMap.ColorProvider color;
private final Color color;
private final ColorKey colorKey;
private final int fontSize;
private final float[] dash;
private final BasicStroke stroke;
@ -160,6 +161,7 @@ public final class Style {
private Style(Builder builder) {
this.thickness = builder.thickness;
this.filled = builder.filled;
this.colorKey = builder.colorKey;
this.color = builder.color;
this.fontSize = builder.fontSize;
this.fontStyle = builder.fontStyle;
@ -188,7 +190,10 @@ public final class Style {
* @return the color
*/
public Color getColor() {
return color.getColor();
if (colorKey != null)
return ColorScheme.getInstance().getColor(colorKey);
else
return color;
}
/**
@ -276,6 +281,18 @@ public final class Style {
.build();
}
/**
* Creates a new style, based on this style.
*
* @param colorKey the new color
* @return Style the derived style with the given color set.
*/
public Style deriveColor(ColorKey colorKey) {
return new Builder(this)
.setColor(colorKey)
.build();
}
/**
* Creates a new style, based on this style.
*
@ -306,10 +323,25 @@ public final class Style {
.build();
}
/**
* Creates a new style suited for filling polygons, based on this style.
*
* @param colorKey the fill color key
* @return the nes style
*/
public Style deriveFillStyle(ColorKey colorKey) {
return new Builder(this)
.setThickness(0)
.setFilled(true)
.setColor(colorKey)
.build();
}
private static final class Builder {
private int thickness = LINETHICK;
private boolean filled = false;
private ColorMap.ColorProvider color = new ColorMap.ColorByKey(ColorMap.ColorKey.MAIN);
private ColorKey colorKey = ColorKey.MAIN;
private Color color;
private int fontSize = 24;
private float[] dash = null;
private boolean mattersForSize = false;
@ -322,6 +354,7 @@ public final class Style {
private Builder(Style style) {
thickness = style.thickness;
filled = style.filled;
colorKey = style.colorKey;
color = style.color;
fontSize = style.fontSize;
dash = style.getDash();
@ -339,13 +372,15 @@ public final class Style {
return this;
}
private Builder setColor(ColorMap.ColorKey key) {
this.color = new ColorMap.ColorByKey(key);
private Builder setColor(ColorKey key) {
this.colorKey = key;
this.color = null;
return this;
}
private Builder setColor(Color color) {
this.color = () -> color;
this.colorKey = null;
this.color = color;
return this;
}

View File

@ -22,7 +22,7 @@ import static de.neemann.digital.draw.shapes.GenericShape.SIZE2;
*/
public class AsyncClockShape implements Shape {
private static final Style TESTSTYLE = Style.NORMAL.deriveFillStyle(new Color(255, 180, 180, 200));
private static final Style ASYNCSTYLE = Style.NORMAL.deriveFillStyle(ColorKey.ASYNC);
private final String label;
/**
@ -54,7 +54,7 @@ public class AsyncClockShape implements Shape {
.add(SIZE2 + SIZE * 4, SIZE2)
.add(SIZE2 + SIZE * 4, SIZE * 2 + SIZE2)
.add(SIZE2, SIZE * 2 + SIZE2);
graphic.drawPolygon(pol, TESTSTYLE);
graphic.drawPolygon(pol, ASYNCSTYLE);
graphic.drawPolygon(pol, Style.THIN);
graphic.drawText(new Vector(SIZE2 + SIZE * 2, SIZE + SIZE2), "Async", Orientation.CENTERCENTER, Style.NORMAL);
graphic.drawText(new Vector(SIZE2 + SIZE * 2, 0), label, Orientation.CENTERBOTTOM, Style.NORMAL);

View File

@ -11,10 +11,7 @@ import de.neemann.digital.core.element.PinDescriptions;
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.Graphic;
import de.neemann.digital.draw.graphics.Orientation;
import de.neemann.digital.draw.graphics.Style;
import de.neemann.digital.draw.graphics.Vector;
import de.neemann.digital.draw.graphics.*;
import java.awt.*;
@ -59,7 +56,7 @@ public class BreakShape implements Shape {
Vector center = new Vector(2 + SIZE, 0);
Style style = Style.NORMAL;
if (!enabled)
style = Style.NORMAL.deriveColor(Color.LIGHT_GRAY);
style = Style.NORMAL.deriveColor(ColorKey.DISABLED);
graphic.drawCircle(center.sub(RAD), center.add(RAD), style);
graphic.drawLine(center.sub(D1), center.add(D1), style);
graphic.drawLine(center.sub(D2), center.add(D2), style);

View File

@ -47,7 +47,7 @@ public class DipSwitchShape implements Shape {
.add(-SIZE - SIZE2, -SIZE2 + 4)
.add(-SIZE * 3 + 5, -SIZE2 + 4);
private static final Style STYLE = Style.THIN.deriveFillStyle(Color.GRAY);
private static final Style STYLE = Style.THIN.deriveFillStyle(ColorKey.PINS);
private final String label;
private final PinDescriptions outputs;

View File

@ -43,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(ColorMap.getInstance().getColor(ColorMap.ColorKey.GRID));
offStyle = Style.NORMAL.deriveFillStyle(ColorKey.GRID);
size = attr.get(Keys.SEVEN_SEG_SIZE);
}

View File

@ -64,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(ColorMap.getInstance().getColor(ColorMap.ColorKey.GRID));
offStyle = Style.NORMAL.deriveFillStyle(ColorKey.GRID);
size = attr.get(Keys.SEVEN_SEG_SIZE);
}

View File

@ -80,7 +80,7 @@ public class StepperMotorShape implements Shape {
}
private static final Style ERROR_STYLE = Style.NORMAL.deriveColor(Color.RED);
private static final Style ERROR_STYLE = Style.NORMAL.deriveColor(ColorKey.ERROR);
@Override
public void drawTo(Graphic graphic, Style highLight) {

View File

@ -10,9 +10,6 @@ import de.neemann.digital.core.element.PinDescriptions;
import de.neemann.digital.draw.elements.IOState;
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;
import static de.neemann.digital.draw.shapes.GenericShape.SIZE2;
@ -22,7 +19,7 @@ import static de.neemann.digital.draw.shapes.GenericShape.SIZE2;
*/
public class TestCaseShape implements Shape {
private static final Style TESTSTYLE = Style.NORMAL.deriveFillStyle(new Color(180, 255, 180, 200));
private static final Style TESTSTYLE = Style.NORMAL.deriveFillStyle(ColorKey.TESTCASE);
private final String label;
/**

View File

@ -8,7 +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 de.neemann.digital.draw.graphics.ColorScheme;
import java.util.ArrayList;
import java.util.Collections;
@ -43,7 +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(ColorScheme.COLOR_SCHEME);
intList.add(Keys.SETTINGS_DEFAULT_TREESELECT);
intList.add(Keys.SETTINGS_GRID);
intList.add(Keys.SETTINGS_SHOW_WIRE_BITS);

View File

@ -43,7 +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.graphics.ColorScheme.*;
import static de.neemann.digital.draw.shapes.GenericShape.SIZE;
import static de.neemann.digital.draw.shapes.GenericShape.SIZE2;

View File

@ -0,0 +1,18 @@
/*
* 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 junit.framework.TestCase;
public class ColorSchemeTest extends TestCase {
public void testCompleteness() {
ColorScheme map = ColorScheme.COLOR_SCHEME.getDefault().getScheme();
for (ColorKey ck : ColorKey.values())
assertNotNull(map.getColor(ck));
}
}