How do I determine if a point on a swing component is visible on the screen?

7

How do I determine if a particular X point inside a swing component is visible on the user's screen?

For example, let's assume that% w / w has been added to a B window (usually, but not necessarily, a% w of%). >

  • Using the JComponent method is easy. If this is false, then the component is not visible and thus not the X point inside it. The same goes for the B window.
  • If the B window is minimized, then the A component is not visible and the X
  • If the A component is within a hierarchy of containers where one of them is not visible, then the component is not visible.
  • If the A component is beyond the boundaries of your container, then obviously it is not visible.
  • If the A component is partially outside the container, I can easily compute the intersection rectangle between the component and its containers to see if the X inside the container.
  • If the B window has been dragged partially out of the screen, I can use the AWT% class to find the areas of the monitor (s) of the user and then use this as part of the intersection of rectangles.
  • Within the B window, there may be another D component that overlaps the A component by hiding it in whole or in part. This I can solve by listing all the components of each window and checking z-order .

So far so good, I know it's possible. But there's one more question I can not resolve:

  • The B window may be partially or totally hidden due to another window C of another application above, possibly hiding all or part of the component A .

This last item, I do not know how to do. Anyone have an idea?

Oh yes, let's finally ignore that some windows or components may have transparent or translucent areas that can show the contents of other windows or components underneath. Let's consider that all windows and components are fully opaque rectangles.

I tried to get my hand in the dough and I got to it from here:

PointVisibility.java

package pixelvisibletest;

import java.awt.Component;
import java.awt.IllegalComponentStateException;
import java.awt.Point;
import java.awt.Window;
import java.util.Objects;
import javax.swing.SwingUtilities;

/**
 * @author Victor
 */
public class PointVisibility {

    private PointVisibility() {
    }

    public static boolean isPointVisibleInComponent(Component c, Point componentLocation) {
        Objects.requireNonNull(c);
        Objects.requireNonNull(componentLocation);
        Point p;
        try {
            p = c.getLocationOnScreen();
        } catch (IllegalComponentStateException e) {
            return false;
        }
        p.x += componentLocation.x;
        p.y += componentLocation.y;
        return isScreenPointVisibleInComponent(c, p);
    }

    public static boolean isScreenPointVisibleInComponent(Component c, Point screenLocation) {
        Objects.requireNonNull(c);
        Objects.requireNonNull(screenLocation);
        Component d = findComponentInScreenLocation(screenLocation);
        while (d != null) {
            if (d == c) return true;
            d = d.getParent();
        }
        return false;
    }

    public static Component findComponentInScreenLocation(Point screenLocation) {
        if (screenLocation == null) return null;
        return findComponentInScreenLocation(screenLocation, findWindowInScreenLocation(screenLocation));
    }

    public static Window findWindowInScreenLocation(Point screenLocation) {
        if (screenLocation == null) return null;
        // BUG Não consegue determinar se o ponto screenLocation está ou não obscurecido por outra janela.
        for (Window window : Window.getWindows()) {
            Point compCoords;
            try {
                compCoords = window.getLocationOnScreen();
            } catch (IllegalComponentStateException e) {
                continue;
            }
            Point relativeToWindow = new Point(screenLocation.x - compCoords.x, screenLocation.y - compCoords.y);
            Component inTheSameWindow = window.findComponentAt(relativeToWindow.x, relativeToWindow.y);
            if (inTheSameWindow != null) return window;
        }

        return null;
    }

    static Component findComponentInScreenLocation(Point screenLocation, Window window) {
        if (window == null) return null;
        Point copy = (Point) screenLocation.clone();
        SwingUtilities.convertPointFromScreen(copy, window);
        return SwingUtilities.getDeepestComponentAt(window, copy.x, copy.y);
    }
}

MouseVisibility.java

package pixelvisibletest;

import java.awt.Component;
import java.awt.MouseInfo;
import java.awt.Window;
import java.util.Objects;

/**
 * @author Victor
 */
public class MouseVisibility {

    private MouseVisibility() {
    }

    public static boolean isComponentUnderMouse(Component c) {
        Objects.requireNonNull(c);
        Component d = findComponentUnderMouse();
        while (d != null) {
            if (d == c) return true;
            d = d.getParent();
        }
        return false;
    }

    public static Component findComponentUnderMouse() {
        return PointVisibility.findComponentInScreenLocation(MouseInfo.getPointerInfo().getLocation(), findWindowUnderMouse());
    }

    public static Window findWindowUnderMouse() {
        for (Window window : Window.getWindows()) {
            if (window.getMousePosition(true) != null) return window;
        }
        return null;
    }
}

To test both classes above, I've created this class:

PixelVisibleTest.java

package pixelvisibletest;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.MouseInfo;
import java.awt.Point;
import javax.swing.JComponent;
import javax.swing.JFrame;

/**
 * @author Victor
 */
public class PixelVisibleTest {

    public static void main(String[] args) {
        EventQueue.invokeLater(PixelVisibleTest::go);
    }

    private static void go() {
        JFrame jf = new JFrame();
        jf.setTitle("teste");
        jf.setLayout(null);
        jf.setSize(400, 100);

        JComponent b = new JComponent() {
            @Override
            public void paintComponent(Graphics g) {
                g.setColor(Color.blue);
                g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
                g.setColor(Color.red);
                g.drawRect(5, 5, 3, 3);
                g.setColor(Color.green);
                g.drawRect(6, 6, 1, 1);
            }
        };
        jf.add(b);
        b.setBounds(15, 15, 20, 20);

        jf.setVisible(true);
        jf.setResizable(true);
        jf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        Thread t = new Thread(() -> {
            try {
                while (jf.isVisible()) {
                    EventQueue.invokeLater(() -> {
                        Point mouse = MouseInfo.getPointerInfo().getLocation();
                        boolean r1 = jf == MouseVisibility.findWindowUnderMouse();
                        boolean r2 = b == MouseVisibility.findComponentUnderMouse();
                        boolean r3 = MouseVisibility.isComponentUnderMouse(b);
                        boolean r4 = PointVisibility.isPointVisibleInComponent(b, new Point(6, 6));
                        boolean r5 = PointVisibility.isScreenPointVisibleInComponent(b, mouse);
                        boolean r6 = jf == PointVisibility.findWindowInScreenLocation(mouse);
                        boolean r7 = b == PointVisibility.findComponentInScreenLocation(mouse);
                        jf.setTitle("teste " + sn(r1) + sn(r2) + sn(r3) + sn(r4) + sn(r5) + sn(r6) + sn(r7));
                    });
                    Thread.sleep(5);
                }
            } catch (InterruptedException e) {
                // Deixa a thread morrer.
            }
        });
        t.start();
    }

    private static String sn(boolean b) {
        return b ? "S" : "N";
    }
}

The JFrame method of the isVisible() class determines which component is what is presented under the position where the mouse is. The Toolkit method tests whether a given component is the component under the mouse. These methods work properly.

It occurs that the reference point that matters to me is not always the position of the mouse. It may be some other arbitrary position on the screen. For these cases I have created the methods findComponentUnderMouse() and MouseVisibility in class isComponentUnderMouse(Component) . These methods are analogous to the two methods I quoted above the findComponentInScreenLocation(Point) class with the exception that the reference point taken is not the position of the mouse. These methods do NOT work properly.

In the test class, within the thread, seven conditions are tested, each referring to a method of the isScreenPointVisibleInComponent(Component, Point) and PointVisibility classes. The result of each test is MouseVisibility which is shown in the window title (S = yes = true, N = no = false). When running this class (method PointVisibility ), you simply drag the window to anywhere and exit by moving the mouse to test the operation. The dot MouseVisibility corresponds to the green pixel drawn on the screen inside the red square.

The methods of class boolean are failing when the reference pixel is on the component, but such a pixel is hidden by the window of another application. The code of class main is not able to perceive the existence of windows of other applications. This does not occur with the (6, 6) class because in the depths of AWT, the PointVisibility method ends up calling the PointVisibility method of the MouseVisibility interface. The implementation of this interface is internal, specific to each JVM and / or OS, and usually done in native code, so it is not enough to imitate using any arbitrary point instead of the mouse position.

My goal is to have an implementation of the getMousePosition(boolean) class that is able to perceive when the reference point is obscured by some window of another application. I do not need much of the class isWindowUnderMouse(Window) , I just put it here to serve as a comparison.

    
asked by anonymous 11.01.2015 / 13:57

1 answer

1

I do not know of a solution using only Java, but I know it's possible to do this using the Windows API, and I believe it's also possible to do this in several other toolkits (GTK +, Qt, KDE, similar to those of the Windows API.

In Windows you can do this:

  • Use the GetDesktopWindow function to get the handle of the desktop;
  • Use the GetWindow function with the GW_HWNDFIRST parameter to get the topmost window handle;
  • Use the GetWindowRect function to get the coordinates of the window handle rectangle;
  • Use the GetWindow function with the GW_HWNDNEXT parameter to get the next window handle;
  • Repeat steps 3 and 4 until you get to your window;
  • Use the data you got from other windows to calculate which parts of your window are visible (do not forget to observe the order of the windows);
  • Calculate whether the point you want to check is in one of the parts of the window that is visible;
  • 20.01.2015 / 18:09