4

I want create a rectangle using the whole screen. By using the whole screen, I mean something like this:

Desktop as seen by user

To start, is that even possible in Java, using the whole screen like that? Second, how would I go about doing it? Another thing, I do not want to draw an actual rectangle, I want to create on, as in a new java.awt.Rectangle.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
user1947236
  • 673
  • 3
  • 12
  • 27

2 Answers2

9

nb- First thing to note, this is done using Java 7, creating a transparent window in Java 6 is done differently and is not possible below update 10 (I believe)

Basically, this creates a transparent window, sized and positioned to cover the entire virtual screen (that is, if you have multiple screens, it will cover all of them).

I then use a JPanel as the primary container to capture mouse events and perform paint effects.

The panel is made transparent. This allows what ever is below the panel (and the frame) to remain visible. I've then over painted this with a transparent color (I did this just to highlight the fact).

When you click and drag an area, it is exposed.

enter image description here

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Area;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class MySnipTool {

    public static void main(String[] args) {
        new MySnipTool();
    }

    public MySnipTool() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setUndecorated(true);
                frame.setBackground(new Color(0, 0, 0, 0));
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new CapturePane());
                Rectangle bounds = getVirtualBounds();
                frame.setLocation(bounds.getLocation());
                frame.setSize(bounds.getSize());
                frame.setAlwaysOnTop(true);
                frame.setVisible(true);
            }
        });
    }

    public class CapturePane extends JPanel {

        private Rectangle selectionBounds;
        private Point clickPoint;

        public CapturePane() {
            setOpaque(false);

            MouseAdapter mouseHandler = new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) {
                        System.exit(0);
                    }
                }

                @Override
                public void mousePressed(MouseEvent e) {
                    clickPoint = e.getPoint();
                    selectionBounds = null;
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    clickPoint = null;
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    Point dragPoint = e.getPoint();
                    int x = Math.min(clickPoint.x, dragPoint.x);
                    int y = Math.min(clickPoint.y, dragPoint.y);
                    int width = Math.max(clickPoint.x - dragPoint.x, dragPoint.x - clickPoint.x);
                    int height = Math.max(clickPoint.y - dragPoint.y, dragPoint.y - clickPoint.y);
                    selectionBounds = new Rectangle(x, y, width, height);
                    repaint();
                }
            };

            addMouseListener(mouseHandler);
            addMouseMotionListener(mouseHandler);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(new Color(255, 255, 255, 128));

            Area fill = new Area(new Rectangle(new Point(0, 0), getSize()));
            if (selectionBounds != null) {
                fill.subtract(new Area(selectionBounds));
            }
            g2d.fill(fill);
            if (selectionBounds != null) {
                g2d.setColor(Color.BLACK);
                g2d.draw(selectionBounds);
            }
            g2d.dispose();
        }
    }

    public static Rectangle getVirtualBounds() {
        Rectangle bounds = new Rectangle(0, 0, 0, 0);

        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice lstGDs[] = ge.getScreenDevices();
        for (GraphicsDevice gd : lstGDs) {
            bounds.add(gd.getDefaultConfiguration().getBounds());
        }
        return bounds;
    }
}

Equally, you could just create a transparent frame that the user can resize. You will be responsible for implementing all the resize code yourself, but the solution would still be a viable one.

Updated

You may also need to check to see if the OS/hardware can support transparancy...

GraphicsConfiguration config = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
if (!AWTUtilities.isTranslucencyCapable(config)) {
    System.out.println("Transluceny is not supported");
}

if (!AWTUtilities.isTranslucencySupported(AWTUtilities.Translucency.PERPIXEL_TRANSPARENT)) {
    System.out.println("PerPeixel Transparency is not supported");
}

Updated with alternative approach

This is an alternative approach to the problem. Basically it takes a snap shot of the screen and renders it to the window. This then allows us to control the highlighting/selection as we need.

The drawback to this is that it is a static result, you won't get any currently running animation effects.

enter image description here

import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
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.HeadlessException;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class SnipWithScreenShoot {

    public static void main(String[] args) {
        new SnipWithScreenShoot();
    }

    public SnipWithScreenShoot() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                try {
                    JFrame frame = new JFrame("Test");
                    frame.setUndecorated(true);
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (AWTException exp) {
                    exp.printStackTrace();
                    System.out.println("That sucks");
                }
            }

        });
    }

    public class TestPane extends JPanel {

        private BufferedImage image;
        private Rectangle selection;

        public TestPane() throws AWTException {
            Robot bot = new Robot();
            image = bot.createScreenCapture(getVirtualBounds());

            MouseAdapter handler = new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    selection = new Rectangle(e.getPoint());
                    repaint();
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    Point p = e.getPoint();
                    int width = Math.max(selection.x - e.getX(), e.getX() - selection.x);
                    int height = Math.max(selection.y - e.getY(), e.getY() - selection.y);
                    selection.setSize(width, height);
                    repaint();
                }
            };

            addMouseListener(handler);
            addMouseMotionListener(handler);
        }

        @Override
        public Dimension getPreferredSize() {
            return image == null ? super.getPreferredSize() : new Dimension(image.getWidth(), image.getHeight());
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (image != null) {
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.drawImage(image, WIDTH, 0, this);
                if (selection != null) {
                    g2d.setColor(new Color(225, 225, 255, 128));
                    g2d.fill(selection);
                    g2d.setColor(Color.GRAY);
                    g2d.draw(selection);
                }
                g2d.dispose();
            }
        }

    }

    public static Rectangle getVirtualBounds() {

        Rectangle bounds = new Rectangle(0, 0, 0, 0);

        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice lstGDs[] = ge.getScreenDevices();
        for (GraphicsDevice gd : lstGDs) {

            bounds.add(gd.getDefaultConfiguration().getBounds());

        }

        return bounds;

    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thanks so much for this! What part of this is what actually requires Java 7? It seems that I got everything to work on Java6 except for the fact that when I drag the rectangle, the color inside of the rectangle is still the color of the transparent jframe. I managed to set the JFrame transparent using AWTUtilities.setWindowOpacity(capture ,(float)0.40); – user1947236 Apr 12 '13 at 22:56
  • I'd use `AWTUtilities.setWindowOpaque(frame, false);` instead, you want to the window to be transparent, not translucent ;) – MadProgrammer Apr 13 '13 at 01:43
  • This `frame.setBackground(new Color(0, 0, 0, 0));` is how the same thing is achieved in Java 7 – MadProgrammer Apr 13 '13 at 01:44
  • Now drag across it, it will open up an area of selection. If you want to do it the other way, there is a trick, basically you take a screen shot and paint that to the backround of the window (this is how the snip it tool in Windows works) and allow the user to drag a rectangle area over it. Trying to get a window that you can drag to size from nothing (when it's transparent) is enormously difficult – MadProgrammer Apr 13 '13 at 03:02
  • Hmm... It doesnt seem to be showing anything underneath it... I guess users will just need Java 7 :/ Thanks for your help! Is there anything else I can do? – user1947236 Apr 13 '13 at 03:06
  • So, when you click and drag across the surface, it doesn't open a transparent hole? – MadProgrammer Apr 13 '13 at 03:07
  • I seems to work fine for Windows and MacOS X (admittedly, I'm using Java 7 on Mac, so I can't say beyond that) – MadProgrammer Apr 13 '13 at 03:24
  • You may need to check to see if the JVM/OS/Hardware can support per pixel alphering, see the update – MadProgrammer Apr 13 '13 at 08:09
  • I added an alternative approach using a screen shot instead of a transparent window – MadProgrammer Apr 13 '13 at 11:15
  • Works perfectly in 2017. Thank you very much for this. You saved me a lot of time. – Louis Tran Mar 20 '17 at 15:24
0

You could look into creating a transparent fullscreen JFrame.This would not be an ideal technique though.

You can have a look at this to get you started on a real solution.

OlivierLi
  • 2,798
  • 1
  • 23
  • 30