JDK-8192788 : Always on top JFrame disappears after restoration from iconified and fullscreen states
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 8u151
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: os_x
  • CPU: x86_64
  • Submitted: 2017-11-30
  • Updated: 2019-05-13
  • Resolved: 2017-12-01
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_151"
Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Mac OS X El Capitan 10.11.6

A DESCRIPTION OF THE PROBLEM :
A JFrame which has been set to be always on top (using setAlwaysOnTop(true)) disappears after being restored from either 1. being iconified to the OS X dock or 2. the OS X fullscreen effect.

The fullscreen effect requires use of the Apple com.apple.eawt.FullScreenUtilities API, but iconification (minimizing) the frame is a standard JFrame feature so the bug may be present on other operating systems.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Call setAlwaysOnTop(true) on a JFrame.
2. Set the JFrame to be visible.
3. Minimize the JFrame (clicking the yellow jewel on Mac).
4. Click on the dock icon to restore the JFrame.
5. The JFrame disappears.

Disappearing from the fullscreen effect requires use of the com.apple.eawt.FullScreenUtilities API (shown in my example code). The steps to reproduce are the same as with iconification.

1. Call setAlwaysOnTop(true) on a JFrame.
2. Set the JFrame to be visible.
3. Click on the green jewel to fullscreen the JFrame.
4. Click on the green jewel to restore the JFrame to windowed mode.
5. The JFrame disappears.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I didn't expect the JFrame to disappear.
ACTUAL -
The JFrame disappears.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package mcve;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.*;

class AlwaysOnTopDisappears {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(AlwaysOnTopDisappears::createAndShowFrame);
    }
    
    static void createAndShowFrame() {
        String osName = System.getProperty("os.name");
        JFrame frame  = new JFrame(osName);
        
        JPanel panel = new JPanel(new BorderLayout());
        panel.setOpaque(true);
        panel.setBackground(Color.WHITE);
        
        JCheckBox check = new JCheckBox("Always On Top", true);
        check.setBorder(BorderFactory.createEmptyBorder(50, 50, 50, 50));
        panel.add(check, BorderLayout.CENTER);
        
        check.addItemListener(e -> frame.setAlwaysOnTop(check.isSelected()));
        
        frame.setContentPane(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setAlwaysOnTop(true);
        frame.pack();
        frame.setLocationRelativeTo(null);
        
        if (osName.startsWith("Mac OS")) {
            enableOsxFullScreen(frame);
            // Uncomment the following line to apply the workarounds.
//            applyDisappearingFrameWorkaround(frame);
        }
        
        frame.setVisible(true);
    }
    
    static void enableOsxFullScreen(JFrame frame) {
        try {
            // Using reflection so the code compiles on non-Mac OS
            Class.forName("com.apple.eawt.FullScreenUtilities")
                 .getMethod("setWindowCanFullScreen", Window.class, boolean.class)
                 .invoke(null, frame, true);
        } catch (ReflectiveOperationException x) {
            x.printStackTrace(System.out);
        }
    }
    
    // Workarounds
    static void applyDisappearingFrameWorkaround(JFrame frame) {
        frame.addWindowStateListener(new WindowStateListener() {
            boolean isIconified(int windowState) {
                return (windowState & Frame.ICONIFIED) != 0;
            }
            @Override
            public void windowStateChanged(WindowEvent e) {
                if (isIconified(e.getOldState()) && !isIconified(e.getNewState())) {
                    if (frame.isAlwaysOnTop() && frame.isVisible()) {
                        frame.setVisible(false);
                        frame.setVisible(true);
                    }
                }
            }
        });
        
        try {
            // If the bug seems to be present on a non-Mac OS,
            // the following could also be accomplished with a
            // window state listener.
            InvocationHandler fullScreenListenerHandler = new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) {
                    if ("windowExitedFullScreen".equals(method.getName())) {
                        if (frame.isAlwaysOnTop()) {
                            frame.toFront();
                        }
                    }
                    return null;
                }
            };
            
            Class<?> fullScreenListener =
                Class.forName("com.apple.eawt.FullScreenListener");
            Object fullScreenListenerProxy =
                Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
                                       new Class<?>[] { fullScreenListener },
                                       fullScreenListenerHandler);
            
            Class.forName("com.apple.eawt.FullScreenUtilities")
                 .getMethod("addFullScreenListenerTo", Window.class, fullScreenListener)
                 .invoke(null, frame, fullScreenListenerProxy);
        } catch (ReflectiveOperationException x) {
            x.printStackTrace(System.out);
        }
    }
}


---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
A workaround can be found at the bottom of the test case code. After being de-iconified, the only workaround seems to be to call setVisible(false) then setVisible(true). Calling toFront() on the frame seems to work for restoration after the OS X fullscreen effect, but not for de-iconification.


Comments
Closing this as "Not an Issue" based upon the information provided by the submitter. Concluding that the issue is non Java and is related to MAC OS X El Capitan as the higher version of MAC (High Sierra) does not reproduce the same.
01-12-2017

Update received from submitter: ======================= Unfortunately, I don't have access to a newer Mac OS. However, I posted this on StackOverflow and one of the Swing regulars over said they couldn't reproduce the issue on High Sierra (link to the comment). It does seem reasonable to conclude that this is an El Capitan issue then. ========================
01-12-2017

According to description a JFrame which set to be always on top (using setAlwaysOnTop(true)) disappears after being restored from either 1. Minimized as icon to the OS X dock or 2. Maxmizing to fullscreen effect. Reported with: JDK 8u151 MAC OS X El Capitan 10.11.6 Checked this for JDK 8u151 and 9 as well as 10 in MAC OS X 10.13 (High Sierra) by following reproducible steps as mentioned in the report, but couldn't reproduce. Results: ========= 8u151: OK 9: OK 9.0.1: OK The issue is reported with MAC OS X 10.11.6 (El Capitan). However, when checked with current MAC OS version 10.13, it doesn't reproduce. To verify, run the attached test case with respective JDk versions. Checking with the submitter to confirm the results with higher MAC versions (10.12.6 or 10.13).
30-11-2017