The question I have is exactly same in requirement as How to pass mouse events to applications behind mine in C#/Vista? , but I need the same for a Transparent Java UI. I can easily create a transparent Java UI using 6.0 but couldn't get any info about passing events through the app to any applications(say a browser) behind.
I believe this will answer your question. To run it you will need Java 6 update 10 and above.
I tested it on Windows Vista
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ClickThrough {
public static void main(String[] args) {
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame f = new JFrame("Test");
f.setAlwaysOnTop(true);
Component c = new JPanel() {
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g.create();
g2.setColor(Color.gray);
int w = getWidth();
int h = getHeight();
g2.fillRect(0, 0, w,h);
g2.setComposite(AlphaComposite.Clear);
g2.fillRect(w/4, h/4, w-2*(w/4), h-2*(h/4));
}
};
c.setPreferredSize(new Dimension(300, 300));
f.getContentPane().add(c);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
com.sun.awt.AWTUtilities.setWindowOpaque(f,false);
}
}
Note that you need to either have an undecorated window or one that is decorated by Java alone (not the default OS decoration) otherwise the code won't work.
Savvas' answer helped me perfectly even on MacOS X 10.7.3 using Java 1.6.0_31. Thanks!
The only thing: I additionally had to set
f.setUndecorated(true);
This is not answer, but an update which corrects dangerous issues to the accepted answer as well as providing an example compatible with Java 7+
Per-Pixel alphering checks each pixel in the window to determine if it's transparent or not. If it's transparent, then the mouse events are allowed to pass through it, if its is not transparent, the mouse events will be caught by the window. This is generally an OS level issue.
The example is you provide is actually doing some very dangerous things, first it's painting a translucent color onto a opaque component, this means that Swing doesn't know that it should actually be painting anything under the component and could also result in a number of very nasty paint artifacts, as Swing only knows about opaque and transparent component, it doesn't know about semi-transparent components, so you need to trick the API.
When performing custom painting, you should always call super.paintComponent to ensure that the Graphics context is setup correctly before painting. In your case, you should also make the component transparent using setOpaque and passing it false
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestFrame {
public static void main(String[] args) {
new TestFrame();
}
public TestFrame() {
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.setUndecorated(true);
frame.setAlwaysOnTop(true);
frame.setBackground(new Color(0, 0, 0, 0));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setOpaque(false);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLUE);
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.fill(new Rectangle(0, 0, getWidth(), getHeight()));
g2d.dispose();
}
}
}
You could try using the Robot Class http://java.sun.com/javase/6/docs/api/java/awt/Robot.html which allows you to specify system events to the low level operating system, specifically things like mouse events.
Related
I want to create a line and after 2 sec break another line and so on.
If i draw a line and pause program execution with Thread.sleep() and again draw a line within paintComponent() method then what happens is first the program stops for 2 second and then both lines are drawn simultaneously.
how to overcome this problem?
This is realitvely commong requirement and issue. You should start by having a look at Concurrency in Swing and How to use Swing Timers for some basic information on how to solve the your basic problem.
You should also have a look at Painting in AWT and Swing and Performing Custom Painting for more information about how painting works in Swing
The core design choices you want to focus on are:
Don't block the Event Dispatching Thread, this will prevent it from painting anything or responding to new events
Don't update the UI from outside the context of the EDT
Painting should paint state. It should not be focused on making logical decisions where possible, instead, it should reliant on one or more models to provide it the information it needs to paint itself
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
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<Shape> shapes;
private int yPos = 0;
public TestPane() {
shapes = new ArrayList<>(25);
Timer timer = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
yPos += 10;
Line2D line = new Line2D.Double(0, yPos, getWidth(), yPos);
shapes.add(line);
repaint();
}
});
timer.setInitialDelay(2000);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Shape line : shapes) {
g2d.draw(line);
}
g2d.dispose();
}
}
}
This question already has answers here:
Using Java pack() method
(3 answers)
Closed 7 years ago.
I was messing with some code and came across the pack method which is suppose to find the "optimum" size for a window. But when I tried to out it simply made a window as small as possible even though I drew a circle before calling pack(). Why is that? What elements does pack() look for? I also frequently have found code that uses both pack() and setSize() which Oracle explicitly says not to do. What is the point since they both set size?
Here is that part of the code incase it was me who did something wrong:
public void paint(Graphics g) {
super.paint(g);
g.fillOval(x, y, 30, 30);
}
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame("Extreme Pong");
Game game = new Game();
frame.add(game);
frame.pack();
}
Edit:
Apparently I either was not clear to most of you since the linked answer to the "duplicate" did not answer my question. The mad programmer did answer my question, so thank you to you.
pack makes use of the layout manager API and "asks" the frame's contentPane for it's preferredSize, this allows pack to "pack" the windows decorations around the content. You have to remember, a windows size includes the windows decorations, so a window of 800x600 will have a viewable which is noticeably smaller
If you add a component to the frame whose size is undefined (in the case of JComponent and JPanel which return a preferredSize of 0x0, the frame will be packed to its smallest possible size, not really what you want.
So, for example, something like...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
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 {
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.fillOval(10, 10, 30, 30);
g2d.dispose();
}
}
}
generates a window of 200x222 on my Mac, but the TestPane will be 200x200
You might also like to have a look at Laying Out Components Within a Container, Painting in AWT and Swing and Performing Custom Painting for some more details
Using .pack() and .setSize() no make sense, all depend which one is last one in the code but i recommendable use:
frame.setMinimumSize(new Dimension(x,y));
And please do not forget to add :
frame.setVisible(true);
This question already has an answer here:
Leaving a trace while painting on a transparent JPanel
(1 answer)
Closed 9 years ago.
I asked a similar question here:
Leaving a trace while painting on a transparent JPanel
One person did try to help me, however I think I wasn't able to make myself clear, so he misunderstood what I wanted.
I want to know how I can stop repaint() from erasing my panel. If I delete the setOpaque(false); statement, it seems to stop the erasing- however, I don't get the background color.
Painting in Swing is a destructive process. The Graphics context used by you component to paint itself is a shared resource, that is, the components painted before your component and after it will use the same graphics context.
This means, if you don't clear it, you will see what ever was painted before you...
It is a requirement of the framework that you clear the Graphics context before you start you painting...
To this end, it is expected that when paintComponent is called, you will completely redraw what ever it is you need repainted.
Take a look at Performing Custom Painting and Painting in AWT and Swing
You need to stop fighting the process and start learning to work with it - your life will be much simpler if you do ;)
Updated with an example of a possible basic approach
Basically, this simply creates a random point some where within the confines of the component, adds that point to a List and request that the component be repainted. The paintComponent method simply loops through this list a paints the points, after it calls super.paintComponent to prepare the Graphics context for painting...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class AutoPaint {
public static void main(String[] args) {
new AutoPaint();
}
public AutoPaint() {
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<Point> points = new ArrayList<>(25);
public TestPane() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
points.add(new Point(random(getWidth()), random(getHeight())));
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Point p : points) {
g2d.drawLine(p.x, p.y, p.x, p.y);
}
g2d.dispose();
}
protected int random(int range) {
return (int)Math.round(Math.random() * range);
}
}
}
I was trying to use XOR mode in Graphics to draw a 1bit texture in color against a flat background, when I encountered a behaviour in Graphics I don't understand.
Here is an example of what I mean, isolated:
package teststuff;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
public class XORTest extends JFrame {
public XORTest() {
super("Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 400);
setIgnoreRepaint(true);
setResizable(false);
setVisible(true);
createBufferStrategy(2);
Graphics graphics = getBufferStrategy().getDrawGraphics();
graphics.setColor(Color.black);
graphics.fillRect(0, 0, getWidth(), getHeight());
graphics.setColor(Color.green);
graphics.fillRect(30, 40, 100, 200);
graphics.setXORMode(Color.white); // (*)
graphics.fillRect(60, 80, 100, 200);
graphics.dispose();
getBufferStrategy().show();
}
public static void main(String[] args) {
XORTest test = new XORTest();
}
}
If I uncomment the line marked with (*), two green rectangles are drawn as expected. If I leave it, nothing is drawn into the component, not even the black background or green rectangle that is drawn beforehand. Even more odd, it worked once. I had the color as Color.green instead of white before. After I changed it, it drew as expected. But when I closed the application and started it again, it didn't work anymore and it hasn't since.
Is this a bug in java? In my jre? Undocumented behaviour for Graphics? I'm on Windows and running the example on the jdk7.
Screenshots: Imgur album because it won't let me post 3 links
The third screenshot is the code as it is above, the first with (*) commented and the second is how it looked the one time it worked (I created that in GIMP because I didn't take a screenshot then).
Without a compelling reason to the contrary, it's easier and more reliable to override paintComponent() in JPanel, which is double buffered by default. With a compelling reason, follow the guidelines in BufferStrategy and BufferCapabilities. Also note,
Override getPreferredSize() to specify the preferred size of a component.
Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* #see https://stackoverflow.com/a/16721780/230513
*/
public class Test {
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new XORPanel());
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static class XORPanel extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(190, 320);
}
#Override
protected void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
graphics.setColor(Color.black);
graphics.fillRect(0, 0, getWidth(), getHeight());
graphics.setColor(Color.green);
graphics.fillRect(30, 40, 100, 200);
graphics.setXORMode(Color.white);
graphics.fillRect(60, 80, 100, 200);
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Test().display();
}
});
}
}
According to the Javadoc, JComponent.repaint(long) is supposed to schedule a repaint() sometime in the future. When I try using it it always triggers an immediate repaint. What am I doing wrong?
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Repaint
{
public static final boolean works = false;
private static class CustomComponent extends JPanel
{
private float alpha = 0;
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setComposite(
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
g2d.setPaint(Color.BLACK);
g2d.fillRect(0, 0, getWidth(), getHeight());
alpha += 0.1;
if (alpha > 1)
alpha = 1;
System.out.println("alpha=" + alpha);
if (!works)
repaint(1000);
}
}
public static void main(String[] args)
{
final JFrame frame = new JFrame();
frame.getContentPane().add(new CustomComponent());
frame.setSize(800, 600);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
if (works)
{
new Timer(1000, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
frame.repaint();
}
}).start();
}
}
}
Note that the Javadoc says the method will cause a repaint to happen within (not after) the specified time.
If you want to schedule something to be repainted, then you should be using a Swing Timer. You should not be scheduling painting from withing the paintComponnt(..) method. You can't control when the paintComponent() method is called.
The parameter says tm - maximum time in milliseconds before update it does not say it won't do so immediately also the javadocs say
Repaints the component. If this
component is a lightweight component,
this results in a call to paint
within tm milliseconds.
If you search a little bit you find that this parameter is ignored in derived classes. ;)