So I am currently making a canvas painter in the style of Microsoft paint in Java. One of the features I was currently working on is the color picker. Now I got it to change the icon cursor but the issue comes with its implementation. I wish to get the color of the pixel that the cursor clicked on (which I already got it working). However, When I try to hover the curser over something I drew on the panel, the mouse goes behind the drawing that I made. To better explain this, I have added a Gif of the aforementioned issue.
Here is the code for my program
Main.java
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
new Controller(new View(),new Model());
}
}
View.java
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import javax.imageio.ImageIO;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SpringLayout;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.PanelUI;
import java.awt.FlowLayout;
import java.awt.BorderLayout;
import java.net.URL;
import java.nio.channels.NonReadableChannelException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import org.kordamp.ikonli.swing.FontIcon;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.GridBagConstraints;
public class View extends JFrame implements MouseListener{
protected static int imgNum = new File("src//imgs").listFiles().length;
private Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
private double width = screenSize.getWidth(), height = screenSize.getHeight();
protected static JPanel panel = new JPanel();
private JPanel buttonsPanel = new JPanel(), colPre = new JPanel();
private static JLabel[] toolsButtonSetIcons = new JLabel[imgNum];
private JMenuBar menuBar = new JMenuBar();
private ImageIcon[] icons = new ImageIcon[imgNum];
protected static URL[] iconURLs = new URL[imgNum];
private JMenu menu = new JMenu("File"), edit = new JMenu("Edit");
protected JMenuItem picImport = new JMenuItem("Import Picture"), pencilSizeChangeMenu = new JMenuItem("Change Pen Size");
private GridBagConstraints gbc = new GridBagConstraints();
private JSlider redSlider,greenSlider,blueSlider;
URL[] tempURLArr;
private static String[] toolNameStrings = { "Eraser", "Cutter", "Pencil", "Color Chooser", "Color Picker",
"Transform", "Picture", "Color Slider","Move" };
JLabel imgLabel;
public View() {
}
public void setupDisplay() {
this.setSize(new Dimension((int) width - 20, (int) height - 60));
this.setTitle("Canvas Painter");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
this.setResizable(false);
this.setLocationRelativeTo(null);
this.add(panel);
this.add(buttonsPanel, BorderLayout.WEST);
this.setJMenuBar(menuBar);
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
menu.add(picImport);
menuBar.add(menu);
menuBar.add(edit);
buttonsPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
setupToolButtonSet();
new Controller().setupPicImportMenuAction(picImport, panel,imgLabel);
System.out.printf("JFrame width: %d and height: %d\n", this.getWidth(), this.getHeight());
this.setVisible(true);
}
private void setupToolButtonSet() {
buttonsPanel.setPreferredSize(new Dimension(150, 100));
System.out.println(imgNum);
for (int i = 0; i < imgNum; i++) {
// buttonsPanel.add(Box.createRigidArea(new Dimension(5, 35)));
iconURLs[i] = getClass().getResource(String.format("./imgs/icon%d.png", i));
System.out.println(String.format("./imgs/icon%d.png", i));
icons[i] = new ImageIcon(
new ImageIcon(iconURLs[i]).getImage().getScaledInstance(20, 20, Image.SCALE_DEFAULT));
toolsButtonSetIcons[i] = new JLabel(icons[i]);
toolsButtonSetIcons[i].setToolTipText(toolNameStrings[i]);
buttonsPanel.add(toolsButtonSetIcons[i]);
}
}
@Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
protected static JLabel[] getLabelSet() {
return toolsButtonSetIcons;
}
protected static String[] getLabelNameArray() {
return toolNameStrings;
}
protected static URL[] getURLArray() {return iconURLs;}
protected void displayColorSliderDialogue() {
JPanel colorDialogPanel = new JPanel(), westColorDialoguePanel = new JPanel(), colorPreviewPanel = new JPanel();
JButton colorDialogValidateButton = new JButton("Ok");
JDialog colorDialog = new JDialog(this, "Color Customization");
JSlider rSlider = new JSlider(0, 255), gSlider = new JSlider(0, 255), bSlider = new JSlider(0, 255);
colorDialog.setSize(new Dimension(400, 240));
colorDialog.add(westColorDialoguePanel, BorderLayout.WEST);
colorDialog.add(colorPreviewPanel, BorderLayout.CENTER);
colorDialog.add(colorDialogPanel, BorderLayout.SOUTH);
westColorDialoguePanel.add(Box.createRigidArea(new Dimension(5, 5)));
westColorDialoguePanel.setLayout(new BoxLayout(westColorDialoguePanel, BoxLayout.Y_AXIS));
westColorDialoguePanel.add(new JLabel("R"));
westColorDialoguePanel.add(rSlider);
westColorDialoguePanel.add(Box.createRigidArea(new Dimension(5, 20)));
westColorDialoguePanel.add(new JLabel("G"));
westColorDialoguePanel.add(gSlider);
westColorDialoguePanel.add(Box.createRigidArea(new Dimension(5, 20)));
westColorDialoguePanel.add(new JLabel("B"));
westColorDialoguePanel.add(bSlider);
colorDialogPanel.add(colorDialogValidateButton);
colorPreviewPanel.setBackground(new Color(rSlider.getValue(), gSlider.getValue(), bSlider.getValue()));
new Controller().setupColorChangeDialogAction(rSlider,gSlider,bSlider,colorDialogValidateButton,colorPreviewPanel);
colorDialog.setLocationRelativeTo(null);
colorDialog.setVisible(true);
colorDialog.setResizable(false);
colorDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
}
protected JSlider getRSlider() {return redSlider;}
protected JSlider getGSlider() {return greenSlider;}
protected JSlider getBSlider() {return blueSlider;}
protected JPanel getColPrevLab() {return colPre;}
protected ImageIcon [] getIconArray() {return icons;}
private class Panel extends JPanel {
}
}
Controller.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.TimerTask;
import java.awt.event.MouseListener;
import java.util.Timer;
import java.util.TimerTask;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Robot;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.plaf.ColorUIResource;
import javax.swing.text.AttributeSet.ColorAttribute;
import java.awt.MouseInfo;
import java.awt.AWTException;
import java.awt.Color;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.Toolkit;
/**
*
* @author Malik
*
*/
public class Controller extends View implements MouseListener, MouseMotionListener {
/**
*
*/
private static final long serialVersionUID = 1L;
private Graphics g;
private static Color drawColor;
private JLabel[] buttonLabels = getLabelSet();
private String[] labelNameStrings = getLabelNameArray();
private URL[] urlArray = getURLArray();
private final int ERASER = 0, CUTTER = 1, PENCIL = 2, COLOR_CHOOSER = 3, COLOR_PICKER = 4, TRANSFORM = 5,
PICTURE = 6, COLOR_SLIDER = 7;
private final Color defaultColor = new Color(0, 0, 0);
private static boolean colorChanged = false;
private String currentTool = "pencil";
private JSlider rSlider = super.getRSlider(), gSlider = super.getGSlider(), bSlider = super.getBSlider();
private JPanel colPreview = super.getColPrevLab();
private JLabel imgLb;
private Image image;
public Controller(View v, Model m) {
System.out.println("inside controller");
v.setupDisplay();
setLabelVisibility(buttonLabels);
View.panel.addMouseListener(this);
View.panel.addMouseMotionListener(this);
for (int i = 0; i < buttonLabels.length; i++)
buttonLabels[i].addMouseListener(this);
}
public Controller() {
}
@Override
public void mouseClicked(MouseEvent e) {
for (int i = 0; i < buttonLabels.length; i++) {
if (e.getSource() == buttonLabels[COLOR_CHOOSER]) {
// g.setColor();
System.out.printf("You clicked: %s\n", labelNameStrings[COLOR_CHOOSER]);
drawColor = JColorChooser.showDialog(this, "Select a color", Color.RED);
colorChanged = true;
break;
}
if (e.getSource() == buttonLabels[ERASER]) {
currentTool = "eraser";
System.out.printf("You clicked: %s\n", labelNameStrings[COLOR_CHOOSER]);
break;
}
if (e.getSource() == buttonLabels[PENCIL]) {
currentTool = "pencil";
super.panel.setCursor(null);
System.out.printf("You clicked: %s\n", labelNameStrings[PENCIL]);
break;
}
if (e.getSource() == buttonLabels[COLOR_SLIDER]) {
currentTool = "Color Slider";
super.displayColorSliderDialogue();
System.out.printf("You clicked: %s\n", labelNameStrings[COLOR_SLIDER]);
break;
}
if (e.getSource() == buttonLabels[COLOR_PICKER]) {
currentTool = "Color Picker";
// System.out.println(urlArray[0].toString());
System.out.printf("You clicked: %s", labelNameStrings[COLOR_PICKER]);
changeCursor(super.panel, buttonLabels[COLOR_PICKER], urlArray[COLOR_PICKER]);
super.panel.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
int xValue = MouseInfo.getPointerInfo().getLocation().x;
int yValue = MouseInfo.getPointerInfo().getLocation().y;
Robot robot;
try {
robot = new Robot();
Color color = robot.getPixelColor(xValue, yValue);
System.out.println(color);
panel.revalidate();
} catch (AWTException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
@Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
});
break;
}
}
// View.panel.repaint();
}
@Override
public void mousePressed(MouseEvent e) {
if (e.getSource() == View.panel && currentTool == "pencil") {
g = View.panel.getGraphics();
if (colorChanged == false)
g.setColor(defaultColor);
else
g.setColor(drawColor);
g.fillOval(e.getX() - 15, e.getY() - 20, 30, 30);
System.out.println("Test");
mouseDragged(e);
}
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
for (int i = 0; i < buttonLabels.length; i++) {
buttonLabels[i].setOpaque(true);
if (e.getSource() == buttonLabels[i]) {
buttonLabels[i].setBackground(Color.GRAY);
}
}
}
@Override
public void mouseExited(MouseEvent e) {
for (int i = 0; i < buttonLabels.length; i++) {
if (e.getSource() == buttonLabels[i] && buttonLabels[i].getBackground() == Color.GRAY)
buttonLabels[i].setBackground(new JButton().getBackground());
}
}
@Override
public void mouseDragged(MouseEvent e) {
if (e.getSource() == View.panel && currentTool == "pencil") {
g = View.panel.getGraphics();
if (colorChanged == false)
g.setColor(defaultColor);
else
g.setColor(drawColor);
g.fillOval(e.getX() - 15, e.getY() - 20, 30, 30);
System.out.println("Test");
}
}
@Override
public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
}
private void setLabelVisibility(JLabel[] labelArray) {
for (int i = 0; i < labelArray.length; i++)
labelArray[i].setOpaque(true);
}
public static void setColor(Color c) {
colorChanged = true;
drawColor = c;
}
public void setupColorChangeDialogAction(JSlider r, JSlider g, JSlider b, JButton confirmBtn, JPanel colPreview) {
r.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
colPreview.setBackground(new Color(r.getValue(), g.getValue(), b.getValue()));
}
});
g.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
colPreview.setBackground(new Color(r.getValue(), g.getValue(), b.getValue()));
}
});
b.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
colPreview.setBackground(new Color(r.getValue(), g.getValue(), b.getValue()));
}
});
confirmBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
colPreview.setBackground(new Color(r.getValue(), g.getValue(), b.getValue()));
Controller.setColor(new Color(r.getValue(), g.getValue(), b.getValue()));
dispose();
}
});
}
public void setupPicImportMenuAction(JMenuItem picImportButton, JPanel panel, JLabel imgLbl) {
picImportButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JFileChooser jfc = new JFileChooser();
FileNameExtensionFilter pngOnly = new FileNameExtensionFilter(".png", "png");
FileNameExtensionFilter jpegOnly = new FileNameExtensionFilter(".jpg", "jpg");
jfc.addChoosableFileFilter(pngOnly);
jfc.addChoosableFileFilter(jpegOnly);
jfc.setAcceptAllFileFilterUsed(false);
int response = jfc.showOpenDialog(null);
if (response == JFileChooser.APPROVE_OPTION) {
try {
BufferedImage ii = ImageIO.read(new FileInputStream(jfc.getSelectedFile().getAbsolutePath()));
addImageToCanvas(panel, ii, imgLbl);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.out.println(jfc.getSelectedFile().getAbsolutePath());
}
}
});
}
protected void addImageToCanvas(JPanel panel, BufferedImage bi, JLabel label) {
label = new JLabel(new ImageIcon(bi));
imgLabel = label;
panel.add(label);
panel.revalidate();
label.setOpaque(true);
}
private void changeCursor(JPanel panel, JLabel newCursorImageLabel, URL iconURL) {
Toolkit toolkit = Toolkit.getDefaultToolkit();
image = toolkit.getImage(iconURL);
System.out.println("\ninside changeCursor " + iconURL.toString());
panel.setCursor(Toolkit.getDefaultToolkit().createCustomCursor(image, new Point(0, 0), "img"));
}
public void getClickedComponentColor() {
}
private class drag {
}
private class drop {
}
}
Model.java
public class Model {
}
I have also provided a zip file containing the entire project. The reason for providing this zip file is because my code requires the images that are located in a folder inside the src folder.
If anyone could help me with the issue regarding the mouse going behind the part of screen that has been drawn on that would be much appreciated
I also believe the cursor is on top but black on black. Therefore I see two ways out of this:
Surround your shape with a thin line of white pixels so you can see the cursor regardless of a dark or light background
configure your cursor not to be black/transparent on whatever.Instead make the black pixels just 'invert' the background. I guess this is an XOR operation instead of an overwrite