Ian Finlayson
Ian Finlayson

Reputation: 311

Java Graphics Program Does Not Close as Expected

I am trying to create a kind of simplified graphics library on top of the built-in Java graphics system. I want to use it in teaching so students can create graphical programs without needing to create their own classes, or especially need to use inheritance.

Anyway, as I have it, the windows do not close as I expect them to and I cannot figure out why. The weird thing is that if I have a println in the main program loop it does work. What's going on? Here is a minimal example:

package test;

import javax.swing.JFrame;
import java.awt.event.WindowListener;
import java.awt.event.WindowEvent;
import java.util.Queue;
import java.util.LinkedList;

/** enumeration of the different types of events which a window can produce */
enum EventType {
    Closed,
    KeyPressed,
    KeyReleased,
    MousePressed,
    MouseReleased,
    MouseMoved
}

/** a class which represents an event which a window can produce */
class Event {
    private EventType t;

    /** create a new event of a given type */
    public Event(EventType type) {
        t = type;
    }

    /** check which type of event it is */
    public EventType getType( ) {
        return t;
    }
}

/** a graphics window */
class Window implements WindowListener {
    private JFrame frame;
    private boolean open;
    private Queue<Event> events;

    /** create the window */
    public Window(String title, int width, int height, boolean resizable) {
        frame = new JFrame(title);
        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        frame.setSize(width, height);
        frame.setResizable(resizable);
        frame.setVisible(true);
        frame.addWindowListener(this);
        events = new LinkedList<>( );
        open = true;
    }

    /** checks whether the window is still open or not */
    public boolean isOpen( ) {
        return open;
    }

    /** closes the window */
    public void close( ) {
        open = false;
        frame.dispose( );
    }

    /** returns the next event, or null if there is none */
    public Event pollEvent( ) {
        return events.poll( );
    }

    /* functions which implement window listening */
    public void windowOpened(WindowEvent e) { }
    public void windowIconified(WindowEvent e) { }
    public void windowDeiconified(WindowEvent e) { }
    public void windowActivated(WindowEvent e) { }
    public void windowDeactivated(WindowEvent e) { }
    public void windowClosed(WindowEvent e) { }
    public void windowClosing(WindowEvent e) {
        System.out.println("Adding close event");
        events.add(new Event(EventType.Closed));
    }
}

public class Test {
    public static void main(String args[]) {
        // create the window
        Window window = new Window("Hello world!", 800, 600, false);

        // while the window is open
        while (window.isOpen( )) {
            // check for events
            Event event = window.pollEvent( );
            if (event != null) {
                switch (event.getType( )) {
                    // handle the window close event
                    case Closed:
                        System.out.println("Calling close");
                        window.close( );
                        break;
                }
            }

            // when this line is un-commented, it works as expected???
            //System.out.print('.');
        }
        System.out.println("All done!");
    }
}

Upvotes: 1

Views: 335

Answers (3)

Ian Finlayson
Ian Finlayson

Reputation: 311

OK, I figured it out. The issue was a lack of synchronization between the Java event threads and my main thread. My main thread was constantly calling which pulls an event from the event Queue.

Then the Java event thread would at some point add the close event to the queue. This was not synchronized, so the event was somehow lost. I'm guessing that it was called "during" a check of the queue and was not committed.

Adding the "synchronized" keyword to "windowClosing" which enqueues events, and "pollEvent" fixed the issue.

Thanks all!

Upvotes: 1

martinez314
martinez314

Reputation: 12332

Make sure you are calling dispose() from the EDT. This should work for you:

   switch (event.getType()) {
    // handle the window close event
        case Closed:
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    System.out.println("Calling close");
                    window.close();
                }
            });

            break;
    }

Upvotes: 2

James Wierzba
James Wierzba

Reputation: 17548

Try changing

frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

to

frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

Upvotes: 1

Related Questions