package chatty.gui.components.settings; import chatty.gui.HtmlColors; import chatty.gui.NamedColor; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.colorchooser.AbstractColorChooserPanel; /** * A grid of colors that can be selected by clicking on them and show their * color name and values on hover. * * @author tduva */ class NamedColorsPanel extends AbstractColorChooserPanel { private final List<NamedColor> namedColors = HtmlColors.getNamedColors(); private final ColorArea colorArea = new ColorArea(); private NamedColor selectedColor = null; @Override public void updateChooser() { selectedColor = null; Color color = getColorFromModel(); for (NamedColor namedColor : namedColors) { if (namedColor.equals(color)) { selectedColor = namedColor; break; } } colorArea.repaint(); } @Override protected void buildChooser() { setLayout(new BorderLayout()); //add(new JLabel("Click color to select."), BorderLayout.NORTH); add(colorArea, BorderLayout.CENTER); setPreferredSize(new Dimension(600,300)); setMinimumSize(new Dimension(600,300)); setMaximumSize(new Dimension(600,500)); //System.out.println(namedColors.size()); } @Override public String getDisplayName() { return "Named Colors"; } @Override public Icon getSmallDisplayIcon() { return null; } @Override public Icon getLargeDisplayIcon() { return null; } /** * The actual area where the colors are drawn and can be selected. */ private class ColorArea extends JComponent implements MouseMotionListener, MouseListener { private static final int PADDING = 2; private static final int MARGIN = 10; private static final int MARGIN_TOP = 40; private static final int COLS = 10; private static final int ROWS = 15; private final Font FONT = new Font("Arial", Font.PLAIN, 12); private final Font TITLE_FONT = new Font("Arial", Font.BOLD, 15); private final Color BACKGROUND_COLOR = new Color(250,250,250); private final List<ColorToDraw> colorsToDraw = new ArrayList<>(); private ColorToDraw hoveredColor; private int height; private int width; private int elementWidth; private int elementHeight; private int compHeight; private int compWidth; ColorArea() { addMouseMotionListener(this); addMouseListener(this); makePositions(); } /** * Calculate {@code Rectangle}s to draw the colors in and save the * {@code NamedColor}s and {@code Rectangle}s in {@code ColorToDraw} * objects. */ private void makePositions() { // Only recalculate if size has changed if (compHeight == getHeight() && compWidth == getWidth()) { return; } compHeight = getHeight(); compWidth = getWidth(); int cols = COLS; int rows = ROWS; colorsToDraw.clear(); int padding = PADDING; int margin = MARGIN; int marginTop = MARGIN_TOP; int x = margin; int y = marginTop; width = getWidth() - (cols - 1) * padding - margin*2; height = getHeight() - (rows - 1) * padding - margin - marginTop; elementWidth = width / cols; elementHeight = height / rows; Iterator<NamedColor> it = namedColors.iterator(); for (int col=0;col<cols;col++) { for (int row=0;row<rows;row++) { if (it.hasNext()) { NamedColor c = it.next(); Rectangle r = new Rectangle(x, y, elementWidth, elementHeight); colorsToDraw.add(new ColorToDraw(c, r)); } y += elementHeight + padding; } x += elementWidth + padding; y = marginTop; } } /** * Draw a single {@code ColorToDraw} object, using its {@code Rectangle} * to draw a rectangle of the color. * * @param g * @param color */ private void drawColor(Graphics g, ColorToDraw color) { Rectangle r = color.r; Color c = color.color; int x = r.x; int y = r.y; int w = r.width; int h = r.height; // Draw color rectangle and border g.setColor(c); g.fillRect(x, y, w, h); g.setColor(Color.WHITE); g.drawRect(x, y, w, h); // Draw different border if this is the selected color g.setColor(Color.DARK_GRAY); if (c.equals(selectedColor)) { g.drawRect(x, y, w, h); g.drawRect(x - 1, y - 1, w + 2, h + 2); } } private void drawHoveredColor(Graphics g) { if (hoveredColor == null) { return; } // Get color coordinates Rectangle r = hoveredColor.r; int x = r.x; int y = r.y; int h = r.height; // Create description text and calculate text width and height String text = hoveredColor.color.getName()+" ("+hoveredColor.color.getRgbString()+")"; FontMetrics fontMetrics = g.getFontMetrics(FONT); g.setFont(FONT); int textWidth = fontMetrics.stringWidth(text); int textHeight = fontMetrics.getHeight(); // Change position if text doesn't fit if (x + textWidth > width) { x = x - (x + textWidth - width); } // Draw text background g.setColor(Color.BLACK); g.fillRect(x - 5, y - h - 8, textWidth + 10, textHeight + 8); g.setColor(Color.WHITE); g.fillRect(x - 4, y - h - 7, textWidth + 8, textHeight + 6); // Draw text g.setColor(Color.BLACK); g.drawString(text, x, y - h + textHeight - 7); } private void drawTitle(Graphics g) { FontMetrics fontMetrics = g.getFontMetrics(TITLE_FONT); g.setFont(TITLE_FONT); g.setColor(Color.BLACK); String text = "Click Color to select (X11 colors)"; int textWidth = fontMetrics.stringWidth(text); int y = 10 + fontMetrics.getHeight(); int x = getWidth() / 2 - textWidth / 2; g.drawString(text, x, y); } @Override public void paintComponent(Graphics g) { makePositions(); // Anti-Aliasing Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Draw stuff that should be behind first // Draw background g.setColor(BACKGROUND_COLOR); g.fillRect(0, 0, getWidth(), getHeight()); // Draw title and colors drawTitle(g); for (ColorToDraw c : colorsToDraw) { drawColor(g, c); } drawHoveredColor(g); } /** * Find the {@code ColorToDraw} that is drawn on this {@code Point}. * * @param p The point to get the color from * @return The {@code ColorToDraw} or {@code null} if no color is drawn * at that point */ private ColorToDraw getColorFromPoint(Point p) { for (ColorToDraw color : colorsToDraw) { if (color.r.contains(p)) { return color; } } return null; } @Override public void mouseDragged(MouseEvent e) { } @Override public void mouseMoved(MouseEvent e) { hoveredColor = null; ColorToDraw c = getColorFromPoint(e.getPoint()); if (c != null) { hoveredColor = c; } repaint(); } @Override public void mouseClicked(MouseEvent e) { } @Override public void mousePressed(MouseEvent e) { ColorToDraw c = getColorFromPoint(e.getPoint()); if (c != null) { getColorSelectionModel().setSelectedColor(c.color); } } @Override public void mouseReleased(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } } /** * Saves a {@code NamedColor} together with a {@code Rectangle} where to * draw it. */ private static class ColorToDraw { public final NamedColor color; public final Rectangle r; ColorToDraw(NamedColor color, Rectangle r) { this.color = color; this.r = r; } } }