JLabel mousePressed same label different actions depending on state - java

So I have a label and I want to do the next, as default it starts on red background color, when i do first click (mousePressed) i change the background color to Green.
Now, i want it to turn back to Red once i press for 2nd time once i press it again.
Something like if it is red, turn green.
And if it is green, turn red.
However i don't manage to get it right... I tried something like this:
Object o = evt.getSource();
boolean checkGreen = false;
if (o.equals(lblSI)) {
lblSI.setBackground(Color.GREEN);
checkGreen = true;
}
if (o.equals(lblSI) && checkGreen == true) {
lblSI.setBackground(Color.RED);
}
But it obviously doesnt work since I first turn it green, then red, its an instant change, cant find the right code...

You can use an else to take a different action. However, the state of the green color needs to be part of the object fields, not defined in the action method (as it would be reset to false for each action then).
It might also be more clear if you separate the check for the source and the check for color selection.
... object definition ...
boolean isGreen = false;
... action listener...
Object o = evt.getSource();
if (o.equals(lblSI)) {
if (isGreen) {
lblSI.setBackground(Color.RED);
} else {
lblSI.setBackground(Color.GREEN);
}
isGreen = !isGreen;
}
Adding a complete example that instead sets the foreground color as background color will not work on all platforms.
public class RedGreen implements Runnable {
private JButton press;
#Override
public void run() {
JFrame frame = new JFrame("RedGreen");
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
press = new JButton("Press");
press.addActionListener(new ActionListener() {
boolean isGreen = false;
#Override
public void actionPerformed(ActionEvent e) {
if (isGreen) {
press.setForeground(Color.RED);
} else {
press.setForeground(Color.GREEN);
}
isGreen = !isGreen;
}
});
frame.getContentPane().add(press);
frame.pack();
frame.setVisible(true);
}
public static void main(String...args) throws Exception {
SwingUtilities.invokeAndWait(new RedGreen());
}
}

Here we go for a pretty clean way of doing it. I hope this answers your question.
public class MyLabel extends JLabel implements MouseListener {
private boolean isRed;
private boolean isGreen;
private boolean first_time = true;
public MyLabel(String name) {
super(name);
this.isRed = true;
this.isGreen = false;
this.setOpaque(true);
this.addMouseListener(this);
this.setBackground(Color.red);
}
public void setRed(boolean val) {
isRed = val;
}
public void setGreen(boolean val) {
isGreen = val;
}
public boolean getRed() {
return isRed;
}
public boolean getGreen() {
return isGreen;
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
if (first_time) {
first_time = false;
this.setGreen(true);
this.setRed(false);
}
if (getRed()) {
this.setBackground(Color.red);
this.setGreen(true);
this.setRed(false);
}
else if (getGreen()) {
this.setBackground(Color.green);
this.setGreen(false);
this.setRed(true);
}
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
And the next:
public class TestFrame extends JFrame {
public TestFrame() {
JLabel label = new MyLabel("Name of Label");
this.add(label);
this.setVisible(true);
pack();
}
}
And lastly the main method:
public class Main {
public static void main(String[] args) {
new TestFrame();
}
}
If you have any questions about what I'm doing feel free to ask me. This is how I learnt myself how to code better by asking questions. All the best with the coding :)

Related

How to drag and drop a JLabel into a JPanel

After researching how to use Label DnD I come across with using this solution:
public class LayerItem extends JLabel {
int x = 0, y = 0;
public LayerItem(String text) {
this.setText(text);
this.addMouseMotionListener(new MouseAdapter(){
#Override
public void mouseDragged(MouseEvent evt){
lblMouseDragged(evt);
}
});
}
protected void lblMouseDragged(MouseEvent evt){
this.x = evt.getX();
this.y = evt.getY();
}
}
As the user clicks, and holds, the JLabel the X and Y are recorded as the mouse moves. However, I am stuck on how to know when the click is stopped (ie, user reaches his targeted JPanel) to then move the text into it.
The only bit of reference to allowing JPanels to receive a drop action is by doing something like this:
new JPanel().setDropTarget(getDropTarget());
However, I cannot again find any references on passing through the JLabel as the drop target with the Coords (Absolute layout).
After looking at a few examples posted by #MadProgrammer I came up with a solution that extends both the JPanel and JLabel. Here is the JLabel class:
public class LayerItem extends JLabel {
public LayerItem(String text) {
this.setText(text);
this.setTransferHandler(new ValueExportTransferHandler(text));
this.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
JLabel lbl = (JLabel) e.getSource();
TransferHandler handle = lbl.getTransferHandler();
handle.exportAsDrag(lbl, e, TransferHandler.COPY);
}
});
}
protected static class ValueExportTransferHandler extends TransferHandler {
public static final DataFlavor SUPPORTED_DATE_FLAVOR = DataFlavor.stringFlavor;
private String value;
public ValueExportTransferHandler(String value) {
this.value = value;
}
public String getValue() {
return value;
}
#Override
public int getSourceActions(JComponent c) {
return DnDConstants.ACTION_COPY_OR_MOVE;
}
#Override
protected Transferable createTransferable(JComponent c) {
Transferable t = new StringSelection(getValue());
return t;
}
#Override
protected void exportDone(JComponent source, Transferable data, int action) {
super.exportDone(source, data, action);
// Clean up and remove the LayerItem that was moved
((LayerItem) source).setVisible(false);
((LayerItem) source).getParent().remove((LayerItem) source);
}
}
}
Here is the class for the JPanel:
public class LayerContainer extends JPanel {
public LayerContainer() {
this.setTransferHandler(new ValueImportTransferHandler());
this.setLayout(new GridBagLayout()); // Optional layout
this.setBorder(new CompoundBorder(new LineBorder(Color.DARK_GRAY), new EmptyBorder(20, 20, 20, 20))); // Optional border
}
protected static class ValueImportTransferHandler extends TransferHandler {
public static final DataFlavor SUPPORTED_DATE_FLAVOR = DataFlavor.stringFlavor;
public ValueImportTransferHandler() {
}
#Override
public boolean canImport(TransferHandler.TransferSupport support) {
return support.isDataFlavorSupported(SUPPORTED_DATE_FLAVOR);
}
#Override
public boolean importData(TransferHandler.TransferSupport support) {
boolean accept = false;
if (canImport(support)) {
try {
Transferable t = support.getTransferable();
Object value = t.getTransferData(SUPPORTED_DATE_FLAVOR);
if (value instanceof String) { // Ensure no errors
// TODO: here you can create your own handler
// ie: ((LayerContainer) component).getHandler()....
Component component = support.getComponent();
LayerItem j = new LayerItem((String) value);
((LayerContainer) component).add(j); // Add a new drag JLabel
accept = true;
}
} catch (Exception exp) {
exp.printStackTrace();
}
}
return accept;
}
}
}
Here is an example of how you could use this (drag from one JPanel to another and back again):
JPanel left_panel = new LayerContainer();
panel_1.setBounds(28, 47, 129, 97);
add(panel_1);
LayerContainer right_panel = new LayerContainer();
layerContainer.setBounds(203, 47, 129, 97);
add(layerContainer);
JLabel lblToDrag = new LayerItem("Drag Me");
GridBagConstraints gbc_lblToDrag = new GridBagConstraints();
gbc_lblDragMe.gridx = 0;
gbc_lblDragMe.gridy = 0;
panel_right.add(lblToDrag, gbc_lblToDrag);
For future use, I'll create a onTransfer() method and create my own LayerContainerHandler() which overrites a run() method so each time a Label is moved to different Containers, it execute seperate actions.

Magic movement in game tile by tile

I have a 2D tile game and my hero can use magic(in this case fire), and the goal is to make the fireball move tile by tile until it finds either a wall or an enemy and to make the game stop while the fire is moving. I already have the fire moving and stopping if there is a wall or an enemy(and damaging the enemy). The problem is i can't seem to make the game show the fireball change from tile to tile, which means when i launch the fireball the game automatically shows me the fireball in its last position before collision, and then it disappears from the tiles. Anyone got any ideas as to what I am doing wrong or what i should do to make the game update the fire tile by tile?
(Btw i thought it might have something to do with my observer but I've tried thread.sleep and wait() and it isn't quite working, maybe I am doing it the wrong way).Thank you for your help and if you guys need any code just ask.
public class ImageMatrixGUI extends Observable {
private static final ImageMatrixGUI INSTANCE = new ImageMatrixGUI();
private final String IMAGE_DIR = "images";
private final int SQUARE_SIZE;
private final int N_SQUARES_WIDTH;
private final int N_SQUARES_HEIGHT;
private JFrame frame;
private JPanel panel;
private JPanel info;
private Map<String, ImageIcon> imageDB = new HashMap<String, ImageIcon>();
private List<ImageTile> images = new ArrayList<ImageTile>();
private List<ImageTile> statusImages = new ArrayList<ImageTile>();
private int lastKeyPressed;
private boolean keyPressed;
private ImageMatrixGUI() {
SQUARE_SIZE = 48;
N_SQUARES_WIDTH = 10;
N_SQUARES_HEIGHT = 10;
init();
}
public static ImageMatrixGUI getInstance() {
return INSTANCE;
}
public void setName(final String name) {
frame.setTitle(name);
}
private void init() {
frame = new JFrame();
panel = new RogueWindow();
info = new InfoWindow();
panel.setPreferredSize(new Dimension(N_SQUARES_WIDTH * SQUARE_SIZE, N_SQUARES_HEIGHT * SQUARE_SIZE));
info.setPreferredSize(new Dimension(N_SQUARES_WIDTH * SQUARE_SIZE, SQUARE_SIZE));
info.setBackground(Color.BLACK);
frame.add(panel);
frame.add(info, BorderLayout.NORTH);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initImages();
new KeyWatcher().start();
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
lastKeyPressed = e.getKeyCode();
keyPressed = true;
releaseObserver();
}
});
}
synchronized void releaseObserver() {
notify();
}
synchronized void waitForKey() throws InterruptedException {
while (!keyPressed) {
wait();
}
setChanged();
notifyObservers(lastKeyPressed);
keyPressed = false;
}
private void initImages() {
File dir = new File(IMAGE_DIR);
for (File f : dir.listFiles()) {
assert (f.getName().lastIndexOf('.') != -1);
imageDB.put(f.getName().substring(0, f.getName().lastIndexOf('.')),
new ImageIcon(IMAGE_DIR + "/" + f.getName()));
}
}
public void go() {
frame.setVisible(true);
}
public void newImages(final List<ImageTile> newImages) {
synchronized (images) { // Added 16-Mar-2016
if (newImages == null)
return;
if (newImages.size() == 0)
return;
for (ImageTile i : newImages) {
if (!imageDB.containsKey(i.getName())) {
throw new IllegalArgumentException("No such image in DB " + i.getName());
}
}
images.addAll(newImages);
}
}
public void removeImage(final ImageTile image) {
synchronized (images) {
images.remove(image);
}
}
public void addImage(final ImageTile image) {
synchronized (images) {
images.add(image);
}
}
public void clearImages() {
synchronized (images) {
images.clear();
}
public void newStatusImages(final List<ImageTile> newImages) {
synchronized (statusImages) {
if (newImages == null)
return;
if (newImages.size() == 0)
return;
for (ImageTile i : newImages) {
if (!imageDB.containsKey(i.getName())) {
throw new IllegalArgumentException("No such image in DB " + i.getName());
}
}
statusImages.addAll(newImages);
}
}
public void removeStatusImage(final ImageTile image) {
synchronized (statusImages) {
statusImages.remove(image);
}
public void addStatusImage(final ImageTile image) {
synchronized (statusImages) {
statusImages.add(image);
}
}
public void clearStatus() {
synchronized (statusImages) {
statusImages.clear();
}
}
#SuppressWarnings("serial")
private class RogueWindow extends JPanel {
#Override
public void paintComponent(Graphics g) {
// System.out.println("Thread " + Thread.currentThread() + "
// repainting");
synchronized (images) {
for (ImageTile i : images) {
g.drawImage(imageDB.get(i.getName()).getImage(), i.getPosition().getX() * SQUARE_SIZE,
i.getPosition().getY() * SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE, frame);
}
}
}
}
#SuppressWarnings("serial")
private class InfoWindow extends JPanel {
#Override
public void paintComponent(Graphics g) {
synchronized (statusImages) {
for (ImageTile i : statusImages)
g.drawImage(imageDB.get(i.getName()).getImage(), i.getPosition().getX() * SQUARE_SIZE, 0,
SQUARE_SIZE, SQUARE_SIZE, frame);
}
}
}
private class KeyWatcher extends Thread {
public void run() {
try {
while (true)
waitForKey();
} catch (InterruptedException e) {
}
}
}
public void update() {
frame.repaint();
}
public void dispose() {
images.clear();
statusImages.clear();
imageDB.clear();
frame.dispose();
}
public Dimension getGridDimension() {
return new Dimension(N_SQUARES_WIDTH, N_SQUARES_HEIGHT);
}
}

Java Window pulse-fader works once then breaks

Ok, I've got some code I setup to create a simple little overlay window to use as an alert message for a program I'm working on. Everything works fine the first run through, but trying to run through it again, it freezes the whole thing, forcing me to terminate it via the debugger or task manager. I know I'm doing something wrong, I'm just not sure what, due to my limited experience with Java.
Below is the code I use to setup my window and place it in the lower-right corner above the taskbar:
private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
public static JWindow alertWindow() {
JWindow newWin = new JWindow();
JPanel panel = new JPanel();
BufferedImage img = null;
try {
img = ImageIO.read(Main.class.getResource("/images/test.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
JLabel imgLbl = new JLabel(new ImageIcon(img));
panel.add(imgLbl);
newWin.setContentPane(panel);
newWin.pack();
Insets scnMax = Toolkit.getDefaultToolkit().getScreenInsets(newWin.getGraphicsConfiguration());
int taskBar = scnMax.bottom;
int x = screenSize.width - newWin.getWidth();
int y = screenSize.height - taskBar - newWin.getHeight();
newWin.setLocation(x,y);
newWin.setVisible(true);
final PulseWindow pulseWin = new PulseWindow(newWin);
pulseWin.getWindow().addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent click) {
if(SwingUtilities.isRightMouseButton(click)) {
pulseWin.stopPulsing();
pulseWin.destroyPulse();
} else {
System.out.println(pulseWin.isPulsing());
if(pulseWin.isPulsing()) {pulseWin.stopPulsing();}
else {pulseWin.startPulse();}
}
}
#Override
public void mouseEntered(MouseEvent arg0) {}
#Override
public void mouseExited(MouseEvent arg0) {}
#Override
public void mousePressed(MouseEvent arg0) {}
#Override
public void mouseReleased(MouseEvent arg0) {}
});
pulseWin.startPulsing();
return newWin;
}
And below is the code I've setup to make it pulse to draw the user's attention:
import javax.swing.JWindow;
public class PulseWindow {
private boolean pulse = true;
private boolean doPulse = true;
private Float floor = 0.50f;
private JWindow win;
public PulseWindow(JWindow win) {
this.win = win;
}
public void startPulsing() {
pulse = true;
boolean decreasing = true;
double inc2 = 0.03;
double current = win.getOpacity();
while(pulse) {
if(doPulse) {
if(decreasing) {
current = current - inc2;
if((float) current <= floor) {
current = floor;
win.setOpacity((float) current);
decreasing = false;
} else {
win.setOpacity((float) current);
}
} else {
current = current + inc2;
if((float) current >= 1.0f) {
current = 1.0;
win.setOpacity((float) current);
decreasing = true;
} else {
win.setOpacity((float) current);
}
}
} else {
current = 1.0;
win.setOpacity(1.0f);
decreasing = true;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
win.setOpacity(1.0f);
}
public void destroyPulse() {
pulse = false;
win.dispose();
}
public boolean isPulsing() { return doPulse; }
public void setFloor(float floor) { this.floor = floor; }
public void stopPulsing() { doPulse = false; }
public void startPulse() { doPulse = true; }
public JWindow getWindow() { return win; }
}
Anyway, like I mentioned, it works fine for the first use, but as soon as you close the window via the right-click then attempt to re-run it later (whether by calling the startPulsing() method or by completely reinitializing the whole class with a new JWindow by calling alertWindow() again), the whole program freezes. Any ideas why this is?
Like I said, I'm still a bit of a newbie to Java, so if you see anything else I'm doing wrong/inefficiently, as well, feel free to point it out so I can do it correctly.
Edit:
I'm starting to think the issue is with JWindows, now. I setup some other code for a different method of displaying the alert and, while it doesn't freeze this time, it doesn't work as intended, either.
public class AlertWindow extends JWindow {
private static Border compound = BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(), BorderFactory.createLoweredBevelBorder());
private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
public AlertWindow() {
JPanel panel = new JPanel();
panel.setBorder(compound);
panel.setBackground(Color.RED);
JLabel imgLbl = new JLabel("Enter Alert Msg Here!");
imgLbl.setFont(new Font(null,Font.BOLD,16));
panel.add(imgLbl);
setContentPane(panel);
pack();
this.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent click) {
if(SwingUtilities.isLeftMouseButton(click)) {
scrollOff();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
scrollOn();
}
}
#Override
public void mouseEntered(MouseEvent arg0) {}
#Override
public void mouseExited(MouseEvent arg0) {}
#Override
public void mousePressed(MouseEvent arg0) {}
#Override
public void mouseReleased(MouseEvent arg0) {}
});
scrollOn();
}
public void scrollOn() {
Insets scnMax = Toolkit.getDefaultToolkit().getScreenInsets(getGraphicsConfiguration());
int taskBar = scnMax.bottom;
int x = screenSize.width - getWidth();
int yEnd = screenSize.height - taskBar - getHeight();
int yStart = screenSize.height;
setLocation(x,yStart);
setVisible(true);
int current = yStart;
while(current > yEnd) {
current-=2;
System.out.println(current);
setLocation(x,current);
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void scrollOff() {
int x = screenSize.width - getWidth();
int yEnd = screenSize.height;
int yStart = this.getBounds().y;
setLocation(x,yStart);
int current = yStart;
while(current < yEnd) {
current+=2;
setLocation(x,current);
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
setVisible(false);
}
}
Just like the pulsing window issue, it works as intended the first time, then breaks on subsequent uses. In this case, the only thing that breaks is the scrollOn() command. It scrolls on while invisible, then becomes visible once it reaches its destination. The console output of the position clearly shows that it's moving, but you can't see it until it stops moving.
Edit 2:
And back to feeling dumb... I found the issue (actually found it some time ago but forgot to update this...). The issue ended up being that I was only using the runnable and not placing it inside of a new Thread() object. For some reason I was thinking runnable objects created their own new threads, but once I figured out my mistake, it was an easy fix. Obviously I still have a long ways to go in learning Java...
Edit:
Ok, now I'm annoyed... apparently it still breaks if you attempt to run it from an action listener of some kind. My most recent version of the PulseAlert class (below) that calls into the PulseWindow class shown in the original answer further below:
public class PulseAlert {
private static Border compound = BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(), BorderFactory.createLoweredBevelBorder());
private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
public void runAlert() throws InterruptedException {
final PulseWindow pulseWin = new PulseWindow(alertWindow());
pulseWin.getWindow().addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent click) {
if(SwingUtilities.isRightMouseButton(click)) {
pulseWin.stopPulsing();
pulseWin.destroyPulse();
} else if(SwingUtilities.isLeftMouseButton(click) && pulseWin.isPulsing()) {
pulseWin.stopPulsing();
} else if(SwingUtilities.isLeftMouseButton(click) && !pulseWin.isPulsing()) {
pulseWin.startPulsing();
}
}
#Override
public void mouseEntered(MouseEvent arg0) {}
#Override
public void mouseExited(MouseEvent arg0) {}
#Override
public void mousePressed(MouseEvent arg0) {}
#Override
public void mouseReleased(MouseEvent arg0) {}
});
try {
pulseWin.startPulse();
} catch (Exception e) {
e.printStackTrace();
}
while(pulseWin.pulserActive()) {
Thread.sleep(100);
}
System.out.println("done with second SW");
}
public static JWindow alertWindow() {
System.out.println("Start");
JWindow newWin = new JWindow();
JPanel panel = new JPanel();
panel.setBorder(compound);
panel.setBackground(Color.RED);
JLabel imgLbl = new JLabel("Enter Alert Msg Here!");
imgLbl.setFont(new Font(null,Font.BOLD,16));
panel.add(imgLbl);
newWin.setContentPane(panel);
newWin.pack();
Insets scnMax = Toolkit.getDefaultToolkit().getScreenInsets(newWin.getGraphicsConfiguration());
int taskBar = scnMax.bottom;
int x = screenSize.width - newWin.getWidth();
int y = screenSize.height - taskBar - newWin.getHeight();
newWin.setLocation(x,y);
newWin.setVisible(true);
return newWin;
}
}
And below is how I can call up the alert window - repeatedly, if I like, as long as it's outside of an action listener.
PulseAlert alertPulse = new PulseAlert();
alertPulse.runAlert();
The above code works flawlessly until placed into an action listener of some kind such as:
trayIcon.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
alertPulse.runAlert();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
});
Once the runAlert() method is called from an action listener, the whole thing freezes like it did previously. Runs perfectly fine until then. Any ideas what is causing this? Is this a bug in Java or am I doing something wrong?
Original Answer:
Ok, I feel pretty dumb, now. All I had to do to fix the issue was place the startPulsing() contents into a new runnable and it all works, and as many times as I need it to.
public void startPulsing() throws Exception {
new Runnable() {
#Override
public void run() {
pulse = true;
win.setVisible(true);
boolean decreasing = true;
double inc = 0.05;
double current = win.getOpacity();
while(pulse) {
if(doPulse) {
if(decreasing) {
current = current - inc;
if((float) current <= floor) {
current = floor;
win.setOpacity((float) current);
decreasing = false;
} else {
win.setOpacity((float) current);
}
} else {
current = current + inc;
if((float) current >= 1.0f) {
current = 1.0;
win.setOpacity((float) current);
decreasing = true;
} else {
win.setOpacity((float) current);
}
}
} else {
current = 1.0;
win.setOpacity(1.0f);
decreasing = true;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
win.setOpacity(1.0f);
}
}.run();
}

Multiple Point JSlider, similar to Fixed Width Slider in Microsoft Excel

I want to make a slider in Java, similar to JSlider, but it can have muliple sliders within itself. To be precise, I want to implement the slider used in Fixed Width Functionality in Microsoft Excel(From Data-> Fixed Width).
A normal slider has just one point, which we can drag to various positions. Now if i click within a position at a slider, a new point should get added, and the dragging operation of this point should be independent of the previous ones. Also if I do a double click, the point is removed from the slider.
import java.awt.*;
import java.awt.event.MouseEvent;
import javax.swing.*;
import javax.swing.plaf.basic.BasicSliderUI;
public class MultiSlider extends JComponent
{
public static void main(String[] args)
{ // main method just for showing a usage example
try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); }
catch(Exception ex){}
JFrame f=new JFrame();
final MultiSlider slider = new MultiSlider();
slider.setValue(0, 80);
slider.addValue(20);
f.getContentPane().add(slider);
f.setSize(200, 100);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
public MultiSlider()
{
super.setLayout(null);
addSlider(0);
}
public void setValue(int slider, int value)
{
((JSlider)getComponent(slider)).setValue(value);
}
public void addValue(int value)
{
addSlider(value);
}
#Override
public boolean isOptimizedDrawingEnabled()
{
return false;
}
#Override
public void doLayout()
{
Insets i=getInsets();
int x=i.left, y=i.top, width=getWidth()-x-i.right, height=getHeight()-y-i.bottom;
for(int ix=0, n=getComponentCount(); ix<n; ix++)
getComponent(ix).setBounds(x, y, width, height);
}
class SubSlider extends JSlider
{
private SubSlider active;
#Override
protected void processMouseEvent(MouseEvent e)
{
SubSlider sl=getClosestSlider(e);
if(e.getID()==MouseEvent.MOUSE_PRESSED) active=sl;
else if(e.getID()==MouseEvent.MOUSE_RELEASED) active=null;
if(e.getID()==MouseEvent.MOUSE_CLICKED)
{
if(sl==null && e.getClickCount()==1) addSlider(e.getPoint());
else if(sl!=null && e.getClickCount()==2)
{
removeSlider(sl);
return;
}
}
if(sl!=null) sl.realProcessMouseEvent(e);
}
private void realProcessMouseEvent(MouseEvent e)
{
e.setSource(this);
super.processMouseEvent(e);
}
#Override
protected void processMouseMotionEvent(MouseEvent e)
{
if(e.getID()==MouseEvent.MOUSE_MOVED)
toAllSliders(e);
else
{
if(active==null) active=getClosestSlider(e);
if(active!=null) active.realProcessMouseMotionEvent(e);
}
}
private void realProcessMouseMotionEvent(MouseEvent e)
{
e.setSource(this);
super.processMouseMotionEvent(e);
}
}
final void toAllSliders(MouseEvent e)
{
for(int ix=0, n=getComponentCount(); ix<n; ix++)
((SubSlider)getComponent(ix)).realProcessMouseMotionEvent(e);
}
public void removeSlider(SubSlider sl)
{
if(getComponentCount()<=1) return;// must keep the last slider
remove(sl);
JSlider slider=(JSlider)getComponent(getComponentCount()-1);
slider.setOpaque(true);
slider.setPaintTrack(true);
revalidate();
repaint();
}
final SubSlider getClosestSlider(MouseEvent e)
{
SubSlider s=(SubSlider)getComponent(0);
BasicSliderUI bsUI=(BasicSliderUI)s.getUI();
int value = bsUI.valueForXPosition(e.getX());
if(Math.abs(s.getValue()-value)<=1) return s;
for(int ix=1, n=getComponentCount(); ix<n; ix++)
{
s=(SubSlider)getComponent(ix);
if(Math.abs(s.getValue()-value)<=1) return s;
}
return null;
}
void addSlider(Point point)
{
BasicSliderUI bsUI = (BasicSliderUI)((JSlider)getComponent(0)).getUI();
addSlider(bsUI.valueForXPosition(point.x));
}
void addSlider(int value)
{
final JSlider slider = new SubSlider();
slider.setFocusable(false);
slider.setValue(value);
if(getComponentCount()!=0)
{
slider.setOpaque(false);
slider.setPaintTrack(false);
}
super.add(slider, 0);
revalidate();
repaint();
}
}

Java MouseEvent, check if pressed down

I have a class that implements MouseListener (JPanel). When I click on the panel something happens. What I want is some kind of while-loop that loops as long as left mousebutton is pressed down.
#Override
public void mousePressed(MouseEvent e) {
while(e.isPressedDownD) { // <--
//DO SOMETHING
}
}
This obviously doesn't work, but I hope you understand what I'm trying to achieve.
The whole class for those that are interested:
package control;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import model.GridModel;
import view.GUIView;
public class MapListener implements MouseListener{
private GridModel model;
private GUIView view;
private int posX;
private int posY;
public MapListener(GridModel model, GUIView view) {
this.model = model;
this.view = view;
}
#Override
public void mouseClicked(MouseEvent e) {
posX = e.getX();
posY = e.getY();
model.setMouseAtX(posX);
model.setMouseAtY(posY);
view.paintTile();
System.out.println("X: " + posX + " Y: " + posY);
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent e) {
while(e.getModifiers() == MouseEvent.MOUSE_PRESSED) { //Obviously doesn't work
//DO SOMETHING
}
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
As pointed out by other answers, the place to do your work is not in the mouse event listener methods.
Also there is no explicit "mouse pressed" notion in MouseEvent, so you must track that yourself. I have provided an example of how to do this. Also note the MouseEvent.BUTTON1 references, as this is just to track the state of the left mouse button.
This is where you must start to learn about concurrency. For that reason, I've added in a synchronized method as you need to be aware that funny things happen when multiple threads access properties at the same time, and synchronized is a mechanism for keeping this sane. Consider it further reading beyond the scope of this example.
Untested, but this should work:
volatile private boolean mouseDown = false;
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
mouseDown = true;
initThread();
}
}
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
mouseDown = false;
}
}
volatile private boolean isRunning = false;
private synchronized boolean checkAndMark() {
if (isRunning) return false;
isRunning = true;
return true;
}
private void initThread() {
if (checkAndMark()) {
new Thread() {
public void run() {
do {
//do something
} while (mouseDown);
isRunning = false;
}
}.start();
}
}
Why do so many of these answers wrongly assert that there is no explicit "mouse pressed" notion in MouseEvent?
Although the other commenters are correct that the OP should not be doing all that stuff in an event handler, there are other situations in which querying the button state in a mouse listener is useful. In those cases, you actually CAN determine the button down state. For example:
#Override
public void mouseExited(MouseEvent event) // Or any other mouse event handler...
{
int buttonsDownMask = MouseEvent.BUTTON1_DOWN_MASK
| MouseEvent.BUTTON2_DOWN_MASK
| MouseEvent.BUTTON3_DOWN_MASK; // Or whichever buttons you care about...
if ( (event.getModifiersEx() & buttonsDownMask) != 0 )
System.out.println("Hey! Some button is pressed!");
}
Notice in particular the use of the MouseEvent.getModifiersEx() method, along with MouseEvent.BUTTON1_DOWN_MASK and friends.
You could create a new Thread containing your while loop.
You start that Thread when the mouse button is pressed. You stop it when the mouse button is released.
You shouldn't be doing that in an event handler as no more events will be processed until the event handler exits.
What you want to achieve can be done with a separate worker thread. Create the thread from the mousePressed listener, do whatever you want to do in the thread (this should contain the while loop) and make the thread exit when the mouse is released (your mouseReleased listener should notify the thread).
for example
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ClickListener extends MouseAdapter implements ActionListener {
private final static int clickInterval = (Integer) Toolkit.getDefaultToolkit().getDesktopProperty("awt.multiClickInterval");
private MouseEvent lastEvent;
private Timer timer;
public ClickListener() {
this(clickInterval);
}
public ClickListener(int delay) {
timer = new Timer(delay, this);
}
#Override
public void mouseClicked(MouseEvent e) {
/*if (e.getClickCount() > 2) {
return;
}
lastEvent = e;
if (timer.isRunning()) {
timer.stop();
doubleClick(lastEvent);
} else {
timer.restart();
}*/
if (timer.isRunning() && !e.isConsumed() && e.getClickCount() > 1) {
System.out.println("double");
timer.stop();
} else {
timer.restart();
}
}
#Override
public void actionPerformed(ActionEvent e) {
timer.stop();
singleClick(lastEvent);
}
public void singleClick(MouseEvent e) {
}
public void doubleClick(MouseEvent e) {
}
public static void main(String[] args) {
JFrame frame = new JFrame("Double Click Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addMouseListener(new ClickListener() {
#Override
public void singleClick(MouseEvent e) {
System.out.println("single");
}
#Override
public void doubleClick(MouseEvent e) {
System.out.println("double");
}
});
frame.setPreferredSize(new Dimension(200, 200));
frame.pack();
frame.setVisible(true);
}
}

Categories