Infinite disappear-reappear loop in JFrame java
Asked Answered
D

1

0

As a succession to this post I'm running into an infinite loop that causes my computer to crash. Or, well, not quite: it seems to cause the Java window (JFrame) to keep getting focus/getting iconified and normalized infinitely. As such do not try to run the code as is. The program won't allow you to shut it down, nor can you do so via task manager.

I have given a complete code example, that you can run - as is often advised. As it's a stripped-down version of an actual program, some code may seem redundant (especially the methods in the middle). The methods that are of importance are: the topmost (the constructor), and the two last ones. I'm guessing something is going wrong with the constructor and/or the refresh method.

As Thomas pointed out here, I put all the code in the constructor in a Runnable block, but I guess I did something wrong there.

Of importance is that in the real program, this class doesn't have a main method but I call the Class from another Class, like so:

if(getInstance() == null) {
    initOverview(0);
}

The two last window listeners are set so that when a user re-opens the window, its contents are updated.

I've been looking into the documentation, but I can't figure out where my error is. Am I using the Runnable example wrong? Or is it because of the Window Listener and the setExtendedState (to ensure the "opening" of the window) that trigger each other? If so, how could I solve this?

ANOTHER WARNING: DO NOT EXECUTE THIS CODE, IT WILL MAKE YOUR COMPUTER FLIP OUT

import javax.swing.*;
import java.awt.Color;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.reflect.InvocationTargetException;
import java.awt.BorderLayout;
import java.awt.GridLayout;

public class OverviewTest extends JFrame {

    private static final long serialVersionUID = 1L;
    private static OverviewTest instance = null;

    private static JLabel labelTextOverview;
    private static JTabbedPane tabbedPane;
    private static JPanel mapPane;

    private OverviewTest() {
        try {
            SwingUtilities.invokeAndWait(new Runnable() {
                public void run() {
                    setTitle("Game - Overview");
                    setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
                    setResizable(true);
                    setBounds(100, 100, 960, 720);

                    JPanel contentPane = new JPanel();
                    setContentPane(contentPane);
                    contentPane.setLayout(new BorderLayout(0, 0));

                    tabbedPane = new JTabbedPane(JTabbedPane.TOP);

                    contentPane.add(tabbedPane);

                    /* TAB 1: Entity Overview */
                    labelTextOverview = new JLabel();
                    labelTextOverview.setText(entityOverviewHTML());

                    JScrollPane scrollPaneEntityOverview = new JScrollPane(labelTextOverview);

                    tabbedPane.addTab("Entity overview", null, scrollPaneEntityOverview, null);

                    /* TAB 2: Map */
                    mapPane = new JPanel();
                    mapPane.setLayout(new GridLayout(6, 6, 2, 2));
                    fillMap();

                    tabbedPane.addTab("Map", null, mapPane, null);

                    /* TAB 3: Rules */
                    JLabel labelRules = new JLabel();
                    labelRules.setText(rulesHTML());

                    JScrollPane scrollPaneRules = new JScrollPane(labelRules);

                    tabbedPane.addTab("Rules", null, scrollPaneRules, null);

                    // Immediately show window on creation 
                    setVisible(true);
                    setExtendedState(JFrame.ICONIFIED);
                    setExtendedState(JFrame.NORMAL);

                    // Add window listener so that contents get refreshed on window active/focus
                    addWindowListener(new WindowAdapter() {
                        @Override
                        public void windowClosing(WindowEvent e) {
                            setVisible(false);
                        }
                        @Override
                        public void windowActivated(WindowEvent e){
                            refresh(tabbedPane.getSelectedIndex());
                        }
                        @Override
                        public void windowDeiconified(WindowEvent e){
                            refresh(tabbedPane.getSelectedIndex());
                        }
                    });
                }
            });
        } catch (InvocationTargetException | InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        if(getInstance() == null) {
            initOverview(0);
        }
    }
    private String rulesHTML() {
        StringBuilder sbRules = new StringBuilder();

        sbRules.append("<html><body style='padding: 12px;'>");

        sbRules.append("<h2>Some text for Rules</h2>");
        sbRules.append("<h3>Lorem ipsum</h3>");

        sbRules.append("</body></html>");

        return sbRules.toString();
    }

    private static void fillMap() {
        mapPane.removeAll();
        for (int i = 0; i < 36; i++) {
            JLabel textTile = new JLabel(fillTile(i));
            JScrollPane tile = new JScrollPane(textTile);

            tile.setBorder(BorderFactory.createLineBorder(Color.BLACK));
            mapPane.add(tile);
        }
    }
    private static String fillTile(int i) {
        StringBuilder sbTile = new StringBuilder();

        sbTile.append("<html>");
        sbTile.append("some text");
        sbTile.append("</html>");

        return sbTile.toString();
    }

    /**
     * Reset UI components to system default
     */
    public static void initOverview(int index) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        }
        catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            System.err.println(e);
        }
        instance = new OverviewTest();
        tabbedPane.setSelectedIndex(index);
    }

    public static OverviewTest getInstance() {
        return instance;
    }

    private static String entityOverviewHTML() {
        StringBuilder sb = new StringBuilder();

        sb.append("<html><body style='padding: 12px;'>");

        sb.append("<h2>Some text for Rules</h2>");
        sb.append("<h3>Lorem ipsum</h3>");

        // Normally a loop that runs over getEntityInfo();
        sb.append(getEntityInfo());
        sb.append("</body></html>");

        return sb.toString();
    }

    private static StringBuilder getEntityInfo() {
        StringBuilder sbInfo = new StringBuilder();

        sbInfo.append("this is not a drill, but a test");        

        return sbInfo;
    }

    private static void bringToFront() {
        getInstance().setVisible(true);
        getInstance().setExtendedState(JFrame.ICONIFIED);
        getInstance().setExtendedState(JFrame.NORMAL);
    }

    public static void refresh(int index) {
        labelTextOverview.setText(entityOverviewHTML());
        fillMap();
        tabbedPane.setSelectedIndex(index);
        getInstance().repaint();
        bringToFront();
    }
}
Dubois answered 6/1, 2016 at 11:36 Comment(0)
P
2

I'm not entirely sure why you are invoking both

    getInstance().setExtendedState(JFrame.ICONIFIED);
    getInstance().setExtendedState(JFrame.NORMAL);

but if you remove the first, i.e. simply:

    getInstance().setExtendedState(JFrame.NORMAL);

then it just opens the window once, without the infinite looping you describe.

The reason your code causes an infinite loop is that it minimizes and then unminimizes the frame, which results in the windowActivated method being run: this invokes the refresh method, which invokes the bringToFront method, which minimizes and unminimizes the frame etc.


To actually bring the window to the front, the following works for me, based upon this question:

private static void bringToFront() {                                                             
    getInstance().setVisible(true);                                                              
    getInstance().setExtendedState(JFrame.NORMAL);                                               
    getInstance().toFront();                                                                     
    getInstance().repaint();                                                                     
}
Polycotyledon answered 6/1, 2016 at 11:46 Comment(8)
Hm, I thought I'd seen this on SO somewhere, but I can't seem to find where. Do you know why this was happening, though?Dubois
Edited to explain why the infinite loop occurs.Polycotyledon
I now see why. By first iconifying and then normalizing you ensure that the window is open and active, at the front. Now, with your adjustements, the windows is active (it 'flickers' in the task bar) but it isn't to the front. It would be nice if this functionality could stay in place.Dubois
I tried the top-voted answer and your answer, but it doesn't seem to work for me. O well, if I can't figure it out I'll make another post. I wonder, what version of Windows and/or Java are you using?Dubois
I'm not using Windows. Java 7.Polycotyledon
JFrame.toFront(); is pretty ignored by JFrame, another effect is by using JDialog/JWindow, invokeAndWait is issue there, because just setVisible(true); should be wrapped into invokeLater, nothing elseRoomer
@Roomer Could you elaboratie in your own answer please?Dubois
@AndyTurner I just saw your original comment. Haha.Dubois

© 2022 - 2024 — McMap. All rights reserved.