I am working on a small game using full screen exclusive and I need to be able to receive keyboard input from the player.
In my program, I have a Window which I set to Full Screen Exclusive, and a rendering loop.
Window creation:
private void initialize() {
//This is used for my game loop...
running = true;
//Create the instance variable 'window' here.
window = new Window(null);
//Ignoring OS paint requests...
window.setIgnoreRepaint(true);
//Set the window to full screen exclusive.
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(window);
...
Game Loop:
private void loop() {
Graphics graphics = window.getGraphics();
graphics.setColor(Color.CYAN);
graphics.fillRect(0, 0, 1920, 1080);
graphics.dispose();
}
My Imports:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.Window;
Now, this works fine. Just as it should. It renders a cyan colored rectangle, across my screen, however, I need to be able to detect when the user hits keys e.g. hitting Escape to close the program, and such. I don't know how to do this. :(
I have tried adding a KeyListener to my window (didn't work).
I have tried adding a JPanel to my window and adding a Listener to that (also didn't work).
I have tried requesting focus on my JPanel and doing the same thing above.
I have tried making a JFrame with a KeyListener then passing it into my window's constructor.
I have tried passing the same JFrame with Key Bindings to my window, rather than a KeyListener.
Obviously, none of the above has worked. (Errors weren't thrown, I simply couldn't get the program to output the text in my sysouts when I pressed a key or exit the program using System.exit(int);) I have taken out everything that didn't work for me from the above code; I currently have a window and a game loop. If there are any other ways for me to get Key Input in full screen exclusive, please inform me. (I feel as if there is a conventional way specifically for full screen exclusive, but I haven't found one yet.) Or if you believe there is a way to get one of the methods I have tried to work, (maybe you believe I did something wrong), please let me know. (I am getting somewhat desperate at this point).
Example using Key Bindings. Really simple, also demonstrates the use of DisplayMode if you're so inclined, but once it's running, simply press and hold space, it will update, release it, it will update. Double click to close ;)
import java.awt.DisplayMode;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
JFrame f = new JFrame("Test");
f.setUndecorated(true);
f.add(new TestPane());
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GraphicsDevice device = GraphicsEnvironment
.getLocalGraphicsEnvironment().getDefaultScreenDevice();
if (device.isFullScreenSupported()) {
device.setFullScreenWindow(f);
if (device.isDisplayChangeSupported()) {
try {
List<DisplayMode> matchingModes = new ArrayList<>(25);
DisplayMode[] modes = device.getDisplayModes();
for (DisplayMode mode : modes) {
if (mode.getWidth() == 1280 && mode.getHeight() == 720) {
matchingModes.add(mode);
}
}
if (!matchingModes.isEmpty()) {
for (DisplayMode mode : matchingModes) {
try {
device.setDisplayMode(mode);
System.out.println(mode.getWidth() + "x" + mode.getHeight() + " " + mode.getBitDepth() + " # " + mode.getRefreshRate());
break;
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
System.err.println("!! No matching modes available");
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.err.println("Change display mode not supported");
}
} else {
System.err.println("Full screen not supported");
}
}
public static class TestPane extends JPanel {
private boolean spaced = false;
public TestPane() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
requestFocusInWindow(true);
if (e.getClickCount() == 2) {
SwingUtilities.windowForComponent(TestPane.this).dispose();
}
}
});
InputMap im = getInputMap();
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), "spaced-pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true), "spaced-released");
am.put("spaced-pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
spaced = true;
repaint();
}
});
am.put("spaced-released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
spaced = false;
repaint();
}
});
requestFocusInWindow(true);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
String text = getWidth() + "x" + getHeight();
FontMetrics fm = g.getFontMetrics();
int x = (getWidth() - fm.stringWidth(text)) / 2;
int y = (getHeight() - fm.getHeight()) / 2;
g.drawString(text, x, y + fm.getAscent());
GraphicsDevice device = GraphicsEnvironment
.getLocalGraphicsEnvironment().getDefaultScreenDevice();
DisplayMode mode = device.getDisplayMode();
text = mode.getWidth() + "x" + mode.getHeight() + " " + mode.getBitDepth() + " # " + mode.getRefreshRate();
x = (getWidth() - fm.stringWidth(text)) / 2;
y += fm.getHeight();
g.drawString(text, x, y + fm.getAscent());
text = "Spaced [" + spaced + "]";
x = (getWidth() - fm.stringWidth(text)) / 2;
y += fm.getHeight();
g.drawString(text, x, y + fm.getAscent());
}
}
}
I think the default event handling for keystrokes is that you could only get them from a focusable component, i.e. a text-field. However, referencing this post, you could try using adding a custom KeyEventDispatcher (which I think is the underlying event-handling for AWT) to the KeyboardFocusManager.
Related
I want to make a java application that mirrors the screen of a computer (with Windows 10) and shows it in itself. It's nothing too complex (I think): it's just an app that captures everything that is showed on the computer screen and shows it in itself. But I don't want to mirror the main screen. I want to mirror the secondary screen, the projected screen.
---> Reason why I need this:
I project contents on big screens in church which are almost 20 meters away from me and it's a bit hard to see the content, and there's no space to put another monitor next to mine... That's why I need an app that shows me in the main screen what is being exhibited.
I already searched about it on web and didn't find satisfactory results... The maximum I found was screenshot and literally record the screen and save as MP4 or another extension of your preference.
Someone could help me in this, please?
Conceptually, the idea is pretty simple. You want to use an instance of Robot to capture a snapshot of the screen.
The first thing you need to do though, is get the "area" of the screen you want to capture and this is not as easy it as might sound as Java doesn't seem to provide the "screen names" 🤨 (on Windows GraphicsDevice#getIDstring might return the name, but on MacOS it didn't)
So, I started by trying to look at the screen resolutions and tried to figure out which screen I wanted...
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice lstGDs[] = ge.getScreenDevices();
for (GraphicsDevice gd : lstGDs) {
System.out.println(gd.getDisplayMode());
}
I could have also used getBounds and tried to figure out where each screen was positioned, but what ever works.
Once you have this information, you can use GraphicsDevice#getBounds to get the physical area of the screen, which you can feed to Robot
Now, you need some way to produce and consume those snapshots. You need to be aware of the fact that Swing is not thread safe and is single threaded. This means you can't capture the snapshot from the Event Dispatching Thread and should not try to update the UI from outside of the Event Dispatching Thread. See Concurrency in Swing for more details.
With this in mind, I decided to go with a SwingWorker. It does most of the heavy lifting for us and provides a simplified workflow. See Worker Threads and SwingWorker for more details.
Okay, the next issue to solve, is how to render the screen. The basic concept is just to paint the image onto a component, which isn't that hard, the hard part is scaling the image.
There's been a lot of discussion on the subject, but you could take a look at How do I resize images inside an application when the application window is resized? and Java: maintaining aspect ratio of JPanel background image
The first presents a concept of "scale to fit" and "scale to fill" algorithms and the second presents a better way to scale a image then using Image#getScaledInstance (which the example uses) as getScaledInstance doesn't always present the best result. You could also take a look at Quality of Image after resize very low -- Java which presents some more ideas and discussions.
import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
class Main {
public static void main(String[] args) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice lstGDs[] = ge.getScreenDevices();
for (GraphicsDevice gd : lstGDs) {
System.out.println(gd.getDisplayMode());
}
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private CaptureWorker worker;
private BufferedImage snapshot;
public TestPane() {
}
#Override
public void addNotify() {
super.addNotify();
startCapture();
}
#Override
public void removeNotify() {
super.removeNotify();
stopCapture();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void startCapture() {
try {
stopCapture();
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getScreenDevices()[0];
worker = new CaptureWorker(gd, new CaptureWorker.Observer() {
#Override
public void imageAvaliable(CaptureWorker source, BufferedImage img) {
TestPane.this.snapshot = img;
repaint();
}
});
worker.execute();
} catch (AWTException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
protected void stopCapture() {
if (worker == null) {
return;
}
worker.stop();
worker = null;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (snapshot != null) {
double scaleFactor = Math.min(1d, getScaleFactorToFill(new Dimension(snapshot.getWidth(), snapshot.getHeight()), getSize()));
int scaleWidth = (int) Math.round(snapshot.getWidth() * scaleFactor);
int scaleHeight = (int) Math.round(snapshot.getHeight() * scaleFactor);
Image imageToRender = snapshot.getScaledInstance(scaleWidth, scaleHeight, Image.SCALE_SMOOTH);
int x = (getWidth() - imageToRender.getWidth(this)) / 2;
int y = (getHeight() - imageToRender.getHeight(this)) / 2;
g2d.drawImage(imageToRender, x, y, this);
}
g2d.dispose();
}
public double getScaleFactor(int iMasterSize, int iTargetSize) {
double dScale = 1;
dScale = (double) iTargetSize / (double) iMasterSize;
return dScale;
}
public double getScaleFactorToFit(Dimension original, Dimension toFit) {
double dScale = 1d;
if (original != null && toFit != null) {
double dScaleWidth = getScaleFactor(original.width, toFit.width);
double dScaleHeight = getScaleFactor(original.height, toFit.height);
dScale = Math.min(dScaleHeight, dScaleWidth);
}
return dScale;
}
public double getScaleFactorToFill(Dimension masterSize, Dimension targetSize) {
double dScaleWidth = getScaleFactor(masterSize.width, targetSize.width);
double dScaleHeight = getScaleFactor(masterSize.height, targetSize.height);
double dScale = Math.max(dScaleHeight, dScaleWidth);
return dScale;
}
}
public class CaptureWorker extends SwingWorker<Void, BufferedImage> {
public interface Observer {
public void imageAvaliable(CaptureWorker source, BufferedImage img);
}
private AtomicBoolean keepRunning = new AtomicBoolean(true);
private Robot bot;
private Rectangle captureBounds;
private final Duration interval = Duration.ofMillis(250);
private Observer observer;
public CaptureWorker(GraphicsDevice device, Observer observer) throws AWTException {
captureBounds = device.getDefaultConfiguration().getBounds();
this.observer = observer;
bot = new Robot();
}
public void stop() {
keepRunning.set(false);
}
#Override
protected void process(List<BufferedImage> chunks) {
BufferedImage img = chunks.get(chunks.size() - 1);
observer.imageAvaliable(this, img);
}
#Override
protected Void doInBackground() throws Exception {
try {
while (keepRunning.get()) {
Instant anchor = Instant.now();
System.out.println("Snapshot");
BufferedImage image = bot.createScreenCapture(captureBounds);
System.out.println("Pubish");
publish(image);
Duration duration = Duration.between(anchor, Instant.now());
System.out.println("Took " + duration.toMillis());
duration = duration.minus(interval);
System.out.println("Time remaining " + duration.toMillis());
if (duration.isNegative()) {
long sleepTime = Math.abs(duration.toMillis());
System.out.println("Sleep for " + sleepTime);
Thread.sleep(sleepTime);
}
}
} catch (Exception exp) {
exp.printStackTrace();
}
return null;
}
}
}
Don't forget
You need to know which screen you want to capture. Take a look at the startCapture method, this where I configured the CaptureWorker. I just grabbed the screen at index 0 and was lucky enough to get the screen I wanted 🤪
Also, this is unsupported. This presents the "how to capture a screen and renderer it a window" portion of your question. How you decide which screen or how you might present that information to the user to configure is all up to you
I'm trying to make an overlay for a HTML-based game running in a browser window and created an JFrame which is opaque. I'd like to be able to still play the game whilst having the overlay above the window. I tried some solutions that I've found but those didn't work for me.
I've thought of catching the click-event on my JFrame and "simulating" the click on the game window. But sadly I don't have an idea how thats possible.
My current code is using the JNA libarys to access the position and scale of the window (in my test code Task-Manager).
I'm fine with using another libary or something like that, if it's even possible.
Thats my code so far:
import com.sun.jna.platform.DesktopWindow;
import com.sun.jna.platform.WindowUtils;
import javax.swing.*;
import java.awt.*;
public class Test {
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame("title");
frame.setUndecorated(true);
frame.setBackground(new Color(255, 69, 0, 100));
frame.setAlwaysOnTop(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Rectangle rect = null;
while (true) {
for (DesktopWindow desktopWindow : WindowUtils.getAllWindows(true)) {
if (desktopWindow.getTitle().contains("Task-Manager")) {
rect = desktopWindow.getLocAndSize();
frame.setSize(rect.width - 16, rect.height - 8);
frame.setLocation(rect.x + 8, rect.y);
frame.setVisible(true);
Thread.sleep(10);
}
}
}
}
}
A JFrame is a heavyweight component. There is a window in the host OS GUI system to go with it. The host OS GUI directs mouse events to the window. Perhaps using a lightweight component for your overlay and then disabling mouse events on it would be a better solution.
Your idea of catching the click event and "simulating it" on you game window should be fairly easy. If your JFrame event processing code has a reference to your game engine, it can determine the relative position of the windows and tell your game engine the corresponding point at which it should act as if it received a click. I.e. just call the same method of your game engine for click events that it received normally and also for the simulated ones.
An ugly hack (there is noticeable flicker) would be to hide the window and send the click through with the Robot class... like this:
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.SwingUtilities;
public class ClickThrough extends Frame implements MouseListener, MouseMotionListener {
private final Robot robot;
private Color bgColor = new Color(0x80808080, true);
private Point dragPoint;
public ClickThrough() throws AWTException {
setAlwaysOnTop(true);
robot = new Robot();
}
public static void main(String[] args) throws AWTException {
ClickThrough w = new ClickThrough();
w.setUndecorated(true);
w.setSize(200, 100);
w.setOpacity(0.7f);
w.addMouseListener(w);
w.addMouseMotionListener(w);
w.setVisible(true);
}
#Override
public void paint(Graphics g) {
int w = getWidth();
int h = getHeight();
g.setColor(Color.GRAY);
g.fillRect(0, 0, w, 16);
g.setColor(bgColor);
g.fillRect(0, 16, w, h-16);
g.setColor(Color.BLACK);
g.drawString("Go ahead, click on me...", 20, 50);
}
private void makeHole(MouseEvent e) {
// Tried making a shape with a hole where the mouse was clicked,... didn't work (macOS).
//setShape(windowWithHoleShape);
setVisible(false);
}
private void repairHole(MouseEvent e) {
//setShape(windowShape);
setVisible(true);
}
#Override
public void mousePressed(MouseEvent e) {
Point p = e.getPoint();
// give it a draggable area at the top
if (p.y < 16) {
dragPoint = p;
return;
}
dragPoint = null;
SwingUtilities.convertPointToScreen(p,this);
makeHole(e);
robot.mouseMove(p.x, p.y);
robot.mousePress(InputEvent.getMaskForButton(e.getButton()));
repairHole(e);
}
#Override public void mouseReleased(MouseEvent e) { }
#Override public void mouseClicked(MouseEvent e) { }
#Override public void mouseEntered(MouseEvent e) { }
#Override public void mouseExited(MouseEvent e) { }
#Override
public void mouseDragged(MouseEvent e) {
if (dragPoint != null) {
Point p = e.getPoint();
SwingUtilities.convertPointToScreen(p, this);
p.translate(-dragPoint.x, -dragPoint.y);
setLocation(p);
}
}
#Override
public void mouseMoved(MouseEvent e) { }
}
I tried to see if I could cut a hole in the window by setting the window Shape, but at least on macOS the hole does not allow the mouse events through.
I should also point out that if you switch your GUI framework to JAvaFX, then you have the option of running your HTML-based game UI in a JavaFX WebView, so you can integrate your game and overlay into a single coherent application. You could specifically make your overlay "mouse transparent". IMO that would be a much better approach than hacking around with the mouse events.
The program is an animation that creates a car and/or truck icon on the screen. The way I have it now it isn't working correctly. Specifically, the program is not clicking and dragging right. If one object is not-selected, once clicked on, it will become bolder to show that it is selected. From there we want to be able to drag it and the program will redraw the image wherever the mouse goes. If the image is un-selected, when I click and drag it, it works fine. The problem I am having is if the image is already selected. If the image is already selected, when I move the mouse over to it and click on it in order to move it to a different position, instead of moving, it is deselected instead so no movement occurs. Here is the code for the mousePressed and mouseDragged events. I think that is where the problem is, but I'm not sure what is causing it.
addMouseListener(new
MouseAdapter()
{
public void mousePressed(MouseEvent event)
{
mousePoint = event.getPoint();
for (SceneShape s : shapes)
{
if (s.contains(mousePoint))
s.setSelected(!s.isSelected());
}
repaint();
}
});
addMouseMotionListener(new
MouseMotionAdapter()
{
public void mouseDragged(MouseEvent event)
{
Point lastMousePoint = mousePoint;
mousePoint = event.getPoint();
for (SceneShape s : shapes)
{
if (s.isSelected())
{
double dx
= mousePoint.getX() - lastMousePoint.getX();
double dy
= mousePoint.getY() - lastMousePoint.getY();
s.translate((int) dx, (int) dy);
}
}
repaint();
}
});
Can someone help explain to me what is causing the program to deselect an already selected image when I drag it instead of moving it and how to fix this problem? Thanks.
One of the side effects of a drag operation is the fact that mouseClicked won't be called. Why is this important? Basically you can use this fact to make a decision about whether a object should be deselected or not within the mouseClicked event instead of something like mousePressed or mouseReleased.
It does require you to maintain some information about the current and previous states, so you know whether the object was just selected or was previously selected, but the basic idea works well.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class WhatADrag {
public static void main(String[] args) {
new WhatADrag();
}
public WhatADrag() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private List<Rectangle> boxes;
private Rectangle selected;
public TestPane() {
boxes = new ArrayList<>(25);
int x = 0;
int y = 0;
for (int index = 0; index < 10; index++) {
boxes.add(new Rectangle(x, y, 100, 100));
x += 25;
y += 25;
}
MouseAdapter ma = new MouseAdapter() {
private Rectangle previous;
private Point delta;
#Override
public void mousePressed(MouseEvent e) {
List<Rectangle> reversed = new ArrayList<>(boxes);
Collections.reverse(reversed);
previous = selected;
if (selected == null || !selected.contains(e.getPoint())) {
for (Rectangle box : reversed) {
if (box.contains(e.getPoint())) {
selected = box;
delta = new Point(e.getX() - selected.x, e.getY() - selected.y);
repaint();
break;
}
}
if (selected != null) {
boxes.remove(selected);
boxes.add(boxes.size() - 1, selected);
}
} else if (selected != null) {
delta = new Point(e.getX() - selected.x, e.getY() - selected.y);
}
}
#Override
public void mouseClicked(MouseEvent e) {
if (selected == previous && selected != null && selected.contains(e.getPoint())) {
selected = null;
repaint();
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (selected != null) {
int x = e.getX() - delta.x;
int y = e.getY() - delta.y;
selected.x = x;
selected.y = y;
repaint();
}
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Rectangle box : boxes) {
if (box != selected) {
g2d.setColor(Color.BLUE);
g2d.fill(box);
g2d.setColor(Color.BLACK);
g2d.draw(box);
}
}
if (selected != null) {
g2d.setColor(Color.CYAN);
g2d.fill(selected);
g2d.setColor(Color.BLUE);
g2d.draw(selected);
}
g2d.dispose();
}
}
}
I am just so lost. I am looking at all this code and don't even understand what it is doing.
Yeah, I feel like this about my code a lot.
Basically...
First: mousePressed is called, I reverse my list of objects, because the top most component will be the last in the list (this is my requirement), I store the currently selected object in the previous variable, I use this to determine if there has been a change in selection or not. I check to see if the user clicked the selected object or not, if they did, we can basically skip every thing else. If not, we determine what they clicked, if anything. The delta is simply the difference between the place they clicked and the location of the object, this is used to make the drag more natural
If no drag occurs: mouseClicked is called. We test to see if the selected object is equal to the previous object and if the mouse was clicked within the selected object, if these are true, then the currently selected object should be deselected. Otherwise the user has basically just changed the selection, so we don't want to immediately deselect it.
Else if a drag occurs: mouseDragged is called. We simply check to see if something is selected, we calculate the difference between the current mouse position and the "click offset" and update the position of the selected object
Clear as mud :P
One thing to also remember is mouseReleased will always be called after mousePressed, even if mouseClicked isn't (which is called after mouseReleased when no dragging occurs).
This question already has answers here:
java thread.sleep puts swing ui to sleep too
(3 answers)
Closed 8 years ago.
I'm trying to make a little program which will display a small square in a random place on the screen. Then once I click on the square I want the square to disappear and after a small random time I want another square to appear in another random place on the screen.
Here's my code:
package mouse;
import org.apache.commons.io.FileUtils;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
/**
* Created by owner on 07/11/2014.
*/
public class MouseTrainer extends JFrame implements MouseListener {
File record = new File("src\\main\\resources\\mouseRecord.txt");
Random rand = new Random();
JLabel lbl;
int x = 0;
int y = 0;
Thread recorder;
public MouseTrainer() throws IOException {
super("GFXAccelerator");
recorder = new Thread(new MouseRecorder(record));
BufferedImage img = ImageIO.read(new File("src\\main\\resources\\square.png"));
ImageIcon icon = new ImageIcon(img);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JPanel contentPane = new JPanel();
contentPane.setOpaque(true);
contentPane.setBackground(Color.WHITE);
contentPane.setLayout(null);
lbl = new JLabel();
lbl.setSize(20, 20);
lbl.setIcon(icon);
lbl.setLocation(0, 0);
contentPane.addMouseListener(this);
contentPane.add(lbl);
this.setContentPane(contentPane);
this.setSize(2560, 1390);
this.setVisible(true);
}
public static void main(String[] args) throws InterruptedException, IOException {
MouseTrainer mr = new MouseTrainer();
}
public void randomPosition() throws IOException {
x = rand.nextInt(2540);
y = rand.nextInt(1350);
FileUtils.writeStringToFile(record, "NEW DESTINATION!\n", true);
lbl.setLocation(x, y);
}
private void hideSquare(){
lbl.setBounds(new Rectangle(new Point(3000, 3000), lbl.getPreferredSize()));
}
#Override
public void mouseClicked(MouseEvent e) {
if((e.getX() > x && e.getX() < x+20) && e.getY()>y && e.getY() <y+20){
System.out.println("hello");
hideSquare();
try {
FileUtils.writeStringToFile(record, "Destination clicked: x=" + x + "; y=" + y + "\n", true);
Thread.sleep(500 +rand.nextInt(5000));
randomPosition();
} catch (IOException e1) {
e1.printStackTrace();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
I listen for the mouse click, and on the mouse click I change the location of the square to an area of screen and do a Thread.sleep() to wait and draw the next square. Unfortunately the location doesn't get changed to an off screen location and the square stays in the same place.
Can someone explain why this is happening and perhaps how I can make the square disappear after the user clicks on it and before the next square appears.
Google Thread.sleep and Swing GUI on this site, and you'll see that the same thing is happening here in your program that is happening in all the other similar programs and similar questions.
Don't call Thread.sleep(...) on the Swing EDT as that just puts your whole GUI to sleep.
Do use a Swing Timer instead.
Do Google and search this site for similar questions.
Please check out Lesson: Concurrency in Swing
Also check out the Swing Tutorials
I'm trying to got a tooltip which displays the current progress of a task. So I want that the tooltip text change while the tooltip is displayed. But, when I call setToolTipText() the displayed text remains the same until I exit the mouse from the tooltip component and enter again. And call setToolTipText(null) before doesn't change anything.
Indeed it does not update itself, even when resetting the tooltip to null between calls.
So far, the only trick I found was to simulate a mouse-move event and forward it on the TooltipManager. It makes him think that the mouse has moved and that the tooltip must be relocated. Not pretty, but quite efficient.
Have a look at this demo code which displays a progress in % from 0 to 100:
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.ToolTipManager;
public class TestTooltips {
protected static void initUI() {
JFrame frame = new JFrame("test");
final JLabel label = new JLabel("Label text");
frame.add(label);
frame.pack();
frame.setVisible(true);
Timer t = new Timer(1000, new ActionListener() {
int progress = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (progress > 100) {
progress = 0;
}
label.setToolTipText("Progress: " + progress + " %");
Point locationOnScreen = MouseInfo.getPointerInfo().getLocation();
Point locationOnComponent = new Point(locationOnScreen);
SwingUtilities.convertPointFromScreen(locationOnComponent, label);
if (label.contains(locationOnComponent)) {
ToolTipManager.sharedInstance().mouseMoved(
new MouseEvent(label, -1, System.currentTimeMillis(), 0, locationOnComponent.x, locationOnComponent.y,
locationOnScreen.x, locationOnScreen.y, 0, false, 0));
}
progress++;
}
});
t.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
initUI();
}
});
}
}
Here's a simplified version of Guillaume Polet's answer which is self-contained in a single method. This code assumes one has called component.setToolTip("..."); previously. This code does not show how to periodically update the tooltip to show the progress.
public static void showToolTip(JComponent component)
{
ToolTipManager manager;
MouseEvent event;
Point point;
String message;
JComponent component;
long time;
manager = ToolTipManager.sharedInstance();
time = System.currentTimeMillis() - manager.getInitialDelay() + 1; // So that the tooltip will trigger immediately
point = component.getLocationOnScreen();
event = new MouseEvent(component, -1, time, 0, 0, 0, point.x, point.y, 1, false, 0);
ToolTipManager.
sharedInstance().
mouseMoved(event);
}