Created
August 24, 2018 03:27
-
-
Save unascribed/d69cde369567f00bb13ca13780a6e5a1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.unascribed.materialpicker; | |
import java.util.Arrays; | |
import java.util.Collections; | |
import java.util.List; | |
import java.util.Random; | |
public enum MaterialColor { | |
RED("Red", | |
0xFF_F44336, | |
0xFF_FFEBEE, | |
0xFF_FFCDD2, | |
0xFF_EF9A9A, | |
0xFF_E57373, | |
0xFF_EF5350, | |
0xFF_E53935, | |
0xFF_D32F2F, | |
0xFF_C62828, | |
0xFF_B71C1C, | |
0xFF_FF8A80, | |
0xFF_FF5252, | |
0xFF_FF1744, | |
0xFF_D50000), | |
PINK("Pink", | |
0xFF_E91E63, | |
0xFF_FCE4EC, | |
0xFF_F8BBD0, | |
0xFF_F48FB1, | |
0xFF_F06292, | |
0xFF_EC407A, | |
0xFF_D81B60, | |
0xFF_C2185B, | |
0xFF_AD1457, | |
0xFF_880E4F, | |
0xFF_FF80AB, | |
0xFF_FF4081, | |
0xFF_F50057, | |
0xFF_C51162), | |
PURPLE("Purple", | |
0xFF_9C27B0, | |
0xFF_F3E5F5, | |
0xFF_E1BEE7, | |
0xFF_CE93D8, | |
0xFF_BA68C8, | |
0xFF_AB47BC, | |
0xFF_8E24AA, | |
0xFF_7B1FA2, | |
0xFF_6A1B9A, | |
0xFF_4A148C, | |
0xFF_EA80FC, | |
0xFF_E040FB, | |
0xFF_D500F9, | |
0xFF_AA00FF), | |
DEEP_PURPLE("Deep Purple", | |
0xFF_673AB7, | |
0xFF_EDE7F6, | |
0xFF_D1C4E9, | |
0xFF_B39DDB, | |
0xFF_9575CD, | |
0xFF_7E57C2, | |
0xFF_5E35B1, | |
0xFF_512DA8, | |
0xFF_4527A0, | |
0xFF_311B92, | |
0xFF_B388FF, | |
0xFF_7C4DFF, | |
0xFF_651FFF, | |
0xFF_6200EA), | |
INDIGO("Indigo", | |
0xFF_3F51B5, | |
0xFF_E8EAF6, | |
0xFF_C5CAE9, | |
0xFF_9FA8DA, | |
0xFF_7986CB, | |
0xFF_5C6BC0, | |
0xFF_3949AB, | |
0xFF_303F9F, | |
0xFF_283593, | |
0xFF_1A237E, | |
0xFF_8C9EFF, | |
0xFF_536DFE, | |
0xFF_3D5AFE, | |
0xFF_304FFE), | |
BLUE("Blue", | |
0xFF_2196F3, | |
0xFF_E3F2FD, | |
0xFF_BBDEFB, | |
0xFF_90CAF9, | |
0xFF_64B5F6, | |
0xFF_42A5F5, | |
0xFF_1E88E5, | |
0xFF_1976D2, | |
0xFF_1565C0, | |
0xFF_0D47A1, | |
0xFF_82B1FF, | |
0xFF_448AFF, | |
0xFF_2979FF, | |
0xFF_2962FF), | |
LIGHT_BLUE("Light Blue", | |
0xFF_03A9F4, | |
0xFF_E1F5FE, | |
0xFF_B3E5FC, | |
0xFF_81D4FA, | |
0xFF_4FC3F7, | |
0xFF_29B6F6, | |
0xFF_039BE5, | |
0xFF_0288D1, | |
0xFF_0277BD, | |
0xFF_01579B, | |
0xFF_80D8FF, | |
0xFF_40C4FF, | |
0xFF_00B0FF, | |
0xFF_0091EA), | |
CYAN("Cyan", | |
0xFF_00BCD4, | |
0xFF_E0F7FA, | |
0xFF_B2EBF2, | |
0xFF_80DEEA, | |
0xFF_4DD0E1, | |
0xFF_26C6DA, | |
0xFF_00ACC1, | |
0xFF_0097A7, | |
0xFF_00838F, | |
0xFF_006064, | |
0xFF_84FFFF, | |
0xFF_18FFFF, | |
0xFF_00E5FF, | |
0xFF_00B8D4), | |
TEAL("Teal", | |
0xFF_009688, | |
0xFF_E0F2F1, | |
0xFF_B2DFDB, | |
0xFF_80CBC4, | |
0xFF_4DB6AC, | |
0xFF_26A69A, | |
0xFF_00897B, | |
0xFF_00796B, | |
0xFF_00695C, | |
0xFF_004D40, | |
0xFF_A7FFEB, | |
0xFF_64FFDA, | |
0xFF_1DE9B6, | |
0xFF_00BFA5), | |
GREEN("Green", | |
0xFF_4CAF50, | |
0xFF_E8F5E9, | |
0xFF_C8E6C9, | |
0xFF_A5D6A7, | |
0xFF_81C784, | |
0xFF_66BB6A, | |
0xFF_43A047, | |
0xFF_388E3C, | |
0xFF_2E7D32, | |
0xFF_1B5E20, | |
0xFF_B9F6CA, | |
0xFF_69F0AE, | |
0xFF_00E676, | |
0xFF_00C853), | |
LIGHT_GREEN("Light Green", | |
0xFF_8BC34A, | |
0xFF_F1F8E9, | |
0xFF_DCEDC8, | |
0xFF_C5E1A5, | |
0xFF_AED581, | |
0xFF_9CCC65, | |
0xFF_7CB342, | |
0xFF_689F38, | |
0xFF_558B2F, | |
0xFF_33691E, | |
0xFF_CCFF90, | |
0xFF_B2FF59, | |
0xFF_76FF03, | |
0xFF_64DD17), | |
LIME("Lime", | |
0xFF_CDDC39, | |
0xFF_F9FBE7, | |
0xFF_F0F4C3, | |
0xFF_E6EE9C, | |
0xFF_DCE775, | |
0xFF_D4E157, | |
0xFF_C0CA33, | |
0xFF_AFB42B, | |
0xFF_9E9D24, | |
0xFF_827717, | |
0xFF_F4FF81, | |
0xFF_EEFF41, | |
0xFF_C6FF00, | |
0xFF_AEEA00), | |
YELLOW("Yellow", | |
0xFF_FFEB3B, | |
0xFF_FFFDE7, | |
0xFF_FFF9C4, | |
0xFF_FFF59D, | |
0xFF_FFF176, | |
0xFF_FFEE58, | |
0xFF_FDD835, | |
0xFF_FBC02D, | |
0xFF_F9A825, | |
0xFF_F57F17, | |
0xFF_FFFF8D, | |
0xFF_FFFF00, | |
0xFF_FFEA00, | |
0xFF_FFD600), | |
AMBER("Amber", | |
0xFF_FFC107, | |
0xFF_FFF8E1, | |
0xFF_FFECB3, | |
0xFF_FFE082, | |
0xFF_FFD54F, | |
0xFF_FFCA28, | |
0xFF_FFB300, | |
0xFF_FFA000, | |
0xFF_FF8F00, | |
0xFF_FF6F00, | |
0xFF_FFE57F, | |
0xFF_FFD740, | |
0xFF_FFC400, | |
0xFF_FFAB00), | |
ORANGE("Orange", | |
0xFF_FF9800, | |
0xFF_FFF3E0, | |
0xFF_FFE0B2, | |
0xFF_FFCC80, | |
0xFF_FFB74D, | |
0xFF_FFA726, | |
0xFF_FB8C00, | |
0xFF_F57C00, | |
0xFF_EF6C00, | |
0xFF_E65100, | |
0xFF_FFD180, | |
0xFF_FFAB40, | |
0xFF_FF9100, | |
0xFF_FF6D00), | |
DEEP_ORANGE("Deep Orange", | |
0xFF_FF5722, | |
0xFF_FBE9E7, | |
0xFF_FFCCBC, | |
0xFF_FFAB91, | |
0xFF_FF8A65, | |
0xFF_FF7043, | |
0xFF_F4511E, | |
0xFF_E64A19, | |
0xFF_D84315, | |
0xFF_BF360C, | |
0xFF_FF9E80, | |
0xFF_FF6E40, | |
0xFF_FF3D00, | |
0xFF_DD2C00), | |
BROWN("Brown", | |
0xFF_795548, | |
0xFF_EFEBE9, | |
0xFF_D7CCC8, | |
0xFF_BCAAA4, | |
0xFF_A1887F, | |
0xFF_8D6E63, | |
0xFF_6D4C41, | |
0xFF_5D4037, | |
0xFF_4E342E, | |
0xFF_3E2723), | |
GREY("Grey", | |
0xFF_9E9E9E, | |
0xFF_FAFAFA, | |
0xFF_F5F5F5, | |
0xFF_EEEEEE, | |
0xFF_E0E0E0, | |
0xFF_BDBDBD, | |
0xFF_757575, | |
0xFF_616161, | |
0xFF_424242, | |
0xFF_212121), | |
BLUE_GREY("Blue Grey", | |
0xFF_607D8B, | |
0xFF_ECEFF1, | |
0xFF_CFD8DC, | |
0xFF_B0BEC5, | |
0xFF_90A4AE, | |
0xFF_78909C, | |
0xFF_546E7A, | |
0xFF_455A64, | |
0xFF_37474F, | |
0xFF_263238), | |
; | |
public final String name; | |
public final boolean vibrant; | |
public final int c50; | |
public final int c100; | |
public final int c200; | |
public final int c300; | |
public final int c400; | |
public final int c500; | |
public final int c600; | |
public final int c700; | |
public final int c800; | |
public final int c900; | |
public final int a100; | |
public final int a200; | |
public final int a400; | |
public final int a700; | |
MaterialColor(String name, int c500, int c50, int c100, int c200, | |
int c300, int c400, int c600, int c700, int c800, int c900) { | |
this.name = name; | |
this.c50 = c50; | |
this.c100 = c100; | |
this.c200 = c200; | |
this.c300 = c300; | |
this.c400 = c400; | |
this.c500 = c500; | |
this.c600 = c600; | |
this.c700 = c700; | |
this.c800 = c800; | |
this.c900 = c900; | |
this.a100 = c100; | |
this.a200 = c200; | |
this.a400 = c400; | |
this.a700 = c700; | |
this.vibrant = false; | |
} | |
MaterialColor(String name, int c500, int c50, int c100, int c200, | |
int c300, int c400, int c600, int c700, int c800, int c900, | |
int a100, int a200, int a400, int a700) { | |
this.name = name; | |
this.c50 = c50; | |
this.c100 = c100; | |
this.c200 = c200; | |
this.c300 = c300; | |
this.c400 = c400; | |
this.c500 = c500; | |
this.c600 = c600; | |
this.c700 = c700; | |
this.c800 = c800; | |
this.c900 = c900; | |
this.a100 = a100; | |
this.a200 = a200; | |
this.a400 = a400; | |
this.a700 = a700; | |
this.vibrant = true; | |
} | |
/** | |
* @param index 50, 100, 200, ... | |
* @return a primary color (e.g. 500) | |
*/ | |
public int getPrimaryColor(int index) { | |
switch (index) { | |
case 50 : return c50; | |
case 100: return c100; | |
case 200: return c200; | |
case 300: return c300; | |
case 400: return c400; | |
case 500: return c500; | |
case 600: return c600; | |
case 700: return c700; | |
case 800: return c800; | |
case 900: return c900; | |
} | |
throw new IllegalArgumentException("Invalid color index "+index); | |
} | |
/** | |
* @param index 100, 200, 400, or 700 | |
* @return an accent color (e.g. A200) | |
*/ | |
public int getAccentColor(int index) { | |
if (!vibrant) throw new IllegalArgumentException(this.name+" is not vibrant"); | |
switch (index) { | |
case 100: return a100; | |
case 200: return a200; | |
case 400: return a400; | |
case 700: return a700; | |
} | |
throw new IllegalArgumentException("Invalid accent color index "+index); | |
} | |
private static final MaterialColor[] VALUES_ARR = values(); | |
private static final MaterialColor[] VIBRANT_VALUES; | |
private static final MaterialColor[] DULL_VALUES; | |
public static final List VALUES = Collections.unmodifiableList(Arrays.asList(VALUES_ARR)); | |
static { | |
int vibrantCount = 0; | |
for (MaterialColor mc : VALUES_ARR) { | |
if (mc.vibrant) vibrantCount++; | |
} | |
VIBRANT_VALUES = new MaterialColor[vibrantCount]; | |
DULL_VALUES = new MaterialColor[VALUES_ARR.length-vibrantCount]; | |
int idxVibrant = 0; | |
int idxDull = 0; | |
for (MaterialColor mc : VALUES) { | |
if (mc.vibrant) { | |
VIBRANT_VALUES[idxVibrant] = mc; | |
idxVibrant++; | |
} else { | |
DULL_VALUES[idxDull] = mc; | |
idxDull++; | |
} | |
} | |
} | |
private static final Random rand = new Random(System.nanoTime()^System.currentTimeMillis()); | |
public static MaterialColor random() { | |
return VALUES_ARR[rand.nextInt(VALUES_ARR.length)]; | |
} | |
public static MaterialColor randomVibrant() { | |
return VIBRANT_VALUES[rand.nextInt(VIBRANT_VALUES.length)]; | |
} | |
public static MaterialColor randomDull() { | |
return DULL_VALUES[rand.nextInt(DULL_VALUES.length)]; | |
} | |
public static MaterialColor hash(Object o) { | |
if (o == null) return GREY; | |
return VALUES_ARR[Math.abs(o.hashCode()) % VALUES_ARR.length]; | |
} | |
public static MaterialColor hashVibrant(Object o) { | |
if (o == null) return RED; | |
return VIBRANT_VALUES[Math.abs(o.hashCode()) % VIBRANT_VALUES.length]; | |
} | |
public static MaterialColor hashDull(Object o) { | |
if (o == null) return GREY; | |
return DULL_VALUES[Math.abs(o.hashCode()) % DULL_VALUES.length]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.unascribed.materialpicker; | |
import java.awt.CardLayout; | |
import java.awt.Color; | |
import java.awt.Dimension; | |
import java.awt.Font; | |
import java.awt.Graphics2D; | |
import java.awt.Image; | |
import java.awt.SystemTray; | |
import java.awt.Toolkit; | |
import java.awt.TrayIcon; | |
import java.awt.datatransfer.StringSelection; | |
import java.awt.event.KeyAdapter; | |
import java.awt.event.KeyEvent; | |
import java.awt.event.MouseAdapter; | |
import java.awt.event.MouseEvent; | |
import java.awt.event.WindowEvent; | |
import java.awt.event.WindowFocusListener; | |
import java.awt.image.BufferedImage; | |
import java.util.Locale; | |
import javax.imageio.ImageIO; | |
import javax.swing.Box; | |
import javax.swing.JComponent; | |
import javax.swing.JDialog; | |
import javax.swing.JLabel; | |
import javax.swing.JPanel; | |
import javax.swing.UIManager; | |
import javax.swing.WindowConstants; | |
import javax.swing.border.EmptyBorder; | |
import javax.swing.plaf.synth.SynthLookAndFeel; | |
public class MaterialPicker { | |
private static BufferedImage baseImageLight; | |
private static BufferedImage baseImageDark; | |
private static JDialog dialog; | |
private static TrayIcon icon; | |
private static int lastColor = 0xF44336; | |
private static final int[] shades = { | |
50, 100, 200, 300, 400, 500, 600, 700, 800, 900 | |
}; | |
private static final int[] accentShades = { | |
100, 200, 400, 700 | |
}; | |
public static void main(String[] args) throws Exception { | |
SynthLookAndFeel laf = new SynthLookAndFeel(); | |
UIManager.setLookAndFeel(laf); | |
baseImageLight = ImageIO.read(ClassLoader.getSystemResource("icon_light.png")); | |
baseImageDark = ImageIO.read(ClassLoader.getSystemResource("icon_dark.png")); | |
SystemTray tray = SystemTray.getSystemTray(); | |
icon = new TrayIcon(createImage(0xF44336), "Material Color Picker"); | |
icon.setImageAutoSize(true); | |
tray.add(icon); | |
dialog = new JDialog(); | |
dialog.setUndecorated(true); | |
dialog.setAlwaysOnTop(true); | |
dialog.setAutoRequestFocus(true); | |
dialog.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); | |
dialog.addKeyListener(new KeyAdapter() { | |
@Override | |
public void keyReleased(KeyEvent e) { | |
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { | |
dialog.setVisible(false); | |
} | |
} | |
}); | |
dialog.addWindowFocusListener(new WindowFocusListener() { | |
@Override | |
public void windowLostFocus(WindowEvent e) { | |
dialog.setVisible(false); | |
} | |
@Override | |
public void windowGainedFocus(WindowEvent e) { | |
} | |
}); | |
Box outer = Box.createHorizontalBox(); | |
outer.setBackground(new Color(MaterialColor.BLUE_GREY.c900).darker()); | |
outer.setOpaque(true); | |
JPanel switcher = new JPanel(); | |
CardLayout cardLayout = new CardLayout(); | |
switcher.setLayout(cardLayout); | |
Box selector = Box.createVerticalBox(); | |
for (MaterialColor color : MaterialColor.VALUES) { | |
Box box = Box.createVerticalBox(); | |
box.setBorder(new EmptyBorder(0, 4, 0, 0)); | |
Box title = Box.createVerticalBox(); | |
title.setBorder(new EmptyBorder(8, 8, 8, 8)); | |
title.addMouseListener(new MouseAdapter() { | |
@Override | |
public void mouseReleased(MouseEvent e) { | |
if (e.getButton() == 1) { | |
changeColor(color.c500); | |
} | |
} | |
}); | |
title.setBackground(new Color(color.c500)); | |
JLabel nameLabel = new JLabel(color.name); | |
JLabel shadeLabel = new JLabel("500"); | |
JLabel hexLabel = new JLabel("#"+Integer.toHexString(color.c500).toUpperCase(Locale.ROOT).substring(2)); | |
nameLabel.setFont(nameLabel.getFont().deriveFont(Font.BOLD)); | |
shadeLabel.setFont(shadeLabel.getFont().deriveFont(Font.BOLD)); | |
hexLabel.setFont(Font.decode("Monospaced Bold 12")); | |
Color fg = getTextColorForBackground(title.getBackground()); | |
nameLabel.setForeground(fg); | |
shadeLabel.setForeground(fg); | |
hexLabel.setForeground(fg); | |
Box boxer = Box.createHorizontalBox(); | |
title.add(boxer); | |
boxer.add(nameLabel); | |
boxer.add(Box.createHorizontalGlue()); | |
title.add(Box.createVerticalStrut(30)); | |
Box boxy = Box.createHorizontalBox(); | |
title.add(boxy); | |
boxy.add(shadeLabel); | |
boxy.add(Box.createHorizontalGlue()); | |
boxy.add(hexLabel); | |
title.setOpaque(true); | |
box.add(title); | |
for (int shade : shades) { | |
box.add(createColorButton(""+shade, color.getPrimaryColor(shade))); | |
} | |
if (color.vibrant) { | |
box.add(Box.createVerticalStrut(4)); | |
for (int shade : accentShades) { | |
box.add(createColorButton("A"+shade, color.getAccentColor(shade))); | |
} | |
} else { | |
box.add(createColorButton("1000*", new Color(color.getPrimaryColor(900)).darker().getRGB()|0xFF000000)); | |
box.add(createColorButton("1500*", new Color(color.getPrimaryColor(900)).darker().darker().getRGB()|0xFF000000)); | |
} | |
JComponent compo = (JComponent)Box.createGlue(); | |
compo.setOpaque(true); | |
compo.setBackground(new Color(color.c500)); | |
compo.setForeground(getTextColorForBackground(compo.getBackground())); | |
compo.addMouseListener(new MouseAdapter() { | |
@Override | |
public void mouseReleased(MouseEvent e) { | |
if (e.getButton() == 1) { | |
cardLayout.show(switcher, color.name()); | |
changeColor(color.getPrimaryColor(500)); | |
} | |
} | |
}); | |
selector.add(compo); | |
switcher.add(box, color.name()); | |
} | |
selector.setMinimumSize(new Dimension(16, MaterialColor.VALUES.size()*8)); | |
selector.setPreferredSize(new Dimension(16, 0)); | |
selector.setMaximumSize(new Dimension(16, 32767)); | |
outer.add(selector); | |
outer.add(switcher); | |
outer.setBorder(new EmptyBorder(4, 4, 4, 4)); | |
dialog.setContentPane(outer); | |
dialog.pack(); | |
dialog.setSize(260, dialog.getHeight()); | |
icon.addMouseListener(new MouseAdapter() { | |
@Override | |
public void mouseClicked(MouseEvent e) { | |
if (e.getButton() == 1) { | |
if (dialog.isVisible()) { | |
dialog.setVisible(false); | |
return; | |
} | |
dialog.setLocation(Math.min(e.getXOnScreen(), Toolkit.getDefaultToolkit().getScreenSize().width-dialog.getWidth()), Math.min(e.getYOnScreen(), Toolkit.getDefaultToolkit().getScreenSize().height-dialog.getHeight())); | |
dialog.setVisible(true); | |
} else if (e.getButton() == 2) { | |
changeColor(lastColor); | |
} else if (e.getButton() == 3) { | |
System.exit(0); | |
} | |
} | |
}); | |
} | |
private static Box createColorButton(String shade, int color) { | |
Box button = Box.createHorizontalBox(); | |
button.setBorder(new EmptyBorder(8, 8, 8, 8)); | |
button.addMouseListener(new MouseAdapter() { | |
@Override | |
public void mouseReleased(MouseEvent e) { | |
if (e.getButton() == 1) { | |
changeColor(color); | |
} | |
} | |
}); | |
button.setBackground(new Color(color)); | |
JLabel shadeLabel = new JLabel(shade); | |
JLabel hexLabel = new JLabel("#"+Integer.toHexString(color).toUpperCase(Locale.ROOT).substring(2)); | |
shadeLabel.setFont(shadeLabel.getFont().deriveFont(Font.BOLD)); | |
hexLabel.setFont(Font.decode("Monospaced Bold 12")); | |
Color fg = getTextColorForBackground(button.getBackground()); | |
shadeLabel.setForeground(fg); | |
hexLabel.setForeground(fg); | |
button.add(shadeLabel); | |
button.add(Box.createHorizontalGlue()); | |
button.add(hexLabel); | |
button.setOpaque(true); | |
return button; | |
} | |
protected static void changeColor(int color) { | |
Image img = createImage(color); | |
icon.setImage(img); | |
dialog.setIconImage(img); | |
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection("#"+Integer.toHexString(color).toUpperCase(Locale.ROOT).substring(2)), (a, b) -> {}); | |
} | |
public static float getLuma(Color color) { | |
float r = color.getRed()/255f; | |
float g = color.getGreen()/255f; | |
float b = color.getBlue()/255f; | |
return (0.2126f*r) + (0.7152f*g) + (0.0722f*b); | |
} | |
public static Color getTextColorForBackground(Color bg) { | |
return getLuma(bg) < 0.5 ? Color.WHITE : Color.BLACK; | |
} | |
private static Image createImage(int bg) { | |
BufferedImage nw = new BufferedImage(baseImageLight.getWidth(), baseImageLight.getHeight(), BufferedImage.TYPE_INT_RGB); | |
Graphics2D g2d = nw.createGraphics(); | |
Color c = new Color(bg); | |
g2d.setColor(c); | |
g2d.fillRect(0, 0, baseImageLight.getWidth(), baseImageLight.getHeight()); | |
g2d.drawImage(getLuma(c) < 0.5 ? baseImageLight : baseImageDark, 0, 0, null); | |
g2d.dispose(); | |
return nw; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment