Search code examples
javaswingtaskbaralways-on-topjwindow

JWindow - Always on top, but below system bars?


I've been making a program that monitors a remote system and displays non-invasive alerts in one of the corners of the screen (user preference) to alert the user to changes in the remote system. To do this, I'm using a JWindow so that I can produce a pulsing effect for more important alerts, to draw the user's attention. I also have an alert that scrolls its way onto the screen from off-screen.

The problem I'm having is that, if I don't set these alerts to always on top, they don't always display, but when I do set them always on top, the scrolling option also displays above the taskbar. Is there any way that I can force it to display over all other programs (full-screen programs need not apply), but below the taskbar?

Edit: Here is my code for scrolling the JWindow on/off screen:

Edit2: Updated code to show my incorporation of NESPowerGlove's answer:

public void scrollOn() {
    //Get the normal screen area minus taskbar
    Insets scnMax = Toolkit.getDefaultToolkit().getScreenInsets(getGraphicsConfiguration());
    int taskBar = scnMax.bottom; //Bottom of the normal window area
    int x = screenSize.width - getWidth(); //Horizontal start point of the window
    int yEnd = screenSize.height - taskBar - getHeight(); //where the window will stop
    int yStart = screenSize.height; //Vertical start point of the window
    setLocation(x,yStart); //set window to start location
    int current = yStart; //windows current location
    newHeight = yStart - current; //Set the newHeight field to the clipping start height
    while(current > yEnd) { //loop while window is still enroute to final destination
        current-=2; //increments to move the window, in pixels
        newHeight = yStart - current; //Update the newHeight field to clip the window appropriately based on position
        setLocation(x,current); //move the window to the next position
        try {
            Thread.sleep(30);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//This is basically the reverse of the scrollOn() method
public void scrollOff() {
    Insets scnMax = Toolkit.getDefaultToolkit().getScreenInsets(getGraphicsConfiguration());
    int taskBar = scnMax.bottom;
    int x = screenSize.width - getWidth();
    int yEnd = screenSize.height - taskBar;
    int yStart = this.getBounds().y;
    setLocation(x,yStart);
    int current = yStart;
    newHeight = this.getBounds().height;
    while(current < yEnd) {
        current+=2;
        newHeight = yEnd - current;
        setLocation(x,current);
        try {
            Thread.sleep(30);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //Tells the system the next alert can be triggered
    //This prevents screen clutter from multiple alerts by setting up a queue
    Main.alertControl.setAlertActive(false); 
}

And below is how I'm actually constructing the window (currently). This method is just a placeholder until I get the images to build the final L&F window.

public AlertScroller(String msg) {
    addComponentListener(new ComponentAdapter() {
        @Override
        public void componentMoved(ComponentEvent e) {
            //Sets the area to be shown while the rest is clipped,
            //updating whenever the component moves
            setShape(new Rectangle2D.Double(0, 0, getWidth(), newHeight));
            if(!isVisible())
                setVisible(true);
        }
    });
    setAlwaysOnTop(true);
    JPanel panel = new JPanel();
        panel.setBorder(compound);
        panel.setBackground(Color.yellow);

    JLabel imgLbl = new JLabel(msg);
        imgLbl.setFont(new Font(null,Font.BOLD,16));

    panel.add(imgLbl);
    setContentPane(panel);
    pack();

    this.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent click) {
            if(SwingUtilities.isLeftMouseButton(click)) {
                autoClear = false;
                scrollOff();
            }
        }
    });
}

Solution

  • You could avoid displaying the JWindow over the task bar (or other similar areas you want to avoid) by figuring out the bounds of the usual area that window frames are displayed within:

    Rectangle maxBounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
    

    Edit: More code for animation issue that the author of question has mentioned in the comments section of this answer that he wants also included in answer:

    With this I think you could make the frame undecorated (setUndecorated(true)), then make the background completely transparent and then slowly increase the height of a rectangle you draw in the background which will eventually color in the entire window.

    Here's a link that describes how to make a transparent frame and to provide opaque shaped backgrounds. In your case, change:

    setShape(new Ellipse2D.Double(0,0,getWidth(),getHeight()));
    

    to (and call often to slowly update the position of the Rectangle by updating newHeightOfDisplay first)

    setShape(new Rectangle2D.Double(0, getHeight() - newHeightOfDisplay, getWidth(), newHeightOfDisplay));