Whenever the default UncaughtExceptionHandler catches and exception, I'd like to quit the game as usual and then show a dialog box or something so the user can see the exception.
I tried some stuff with JDialog but it just wouldn't show up, and I'm not very experienced in coding swing. How would you guys do it?
I tried calling this in the method uncaughtException:
public void showExceptionDialog(final Throwable ex, final Thread t) {
final JFrame parent = new JFrame() {
public Dimension getPreferredSize() {
return new Dimension(200,100);
}
};
parent.setTitle("Debugging frame");
parent.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
parent.pack();
parent.setVisible(false);
JPanel panel = new JPanel();
JPanel tiny = new JPanel();
tiny.setLayout(new BoxLayout(tiny, BoxLayout.Y_AXIS));
tiny.add(new JLabel(ex.getClass().getName()));
JTextField field = new JTextField(ex.getMessage(), 15);
field.setEditable(false);
tiny.add(field);
panel.add(tiny);
JButton button = new JButton("details");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
JTextArea ta = new JTextArea(ex.getMessage());
ta.setEditable(false);
ta.setCaretPosition(0);
/**
* Testing an UncaughtExceptionHandler implementation with StandardGame.
*
* No liscense attached, free to use or modify in absolutely any form with or without
* credit given.
*
* @author Andrew Carter
*/
public class TestExceptionHandler {
public static void main(String[] args) throws Exception {
StandardGame game = new StandardGame("A Simple Test");
/**
* Uncaught exception handler showing the use of a Swing dialog.
*/
public static class ExceptionHandler implements UncaughtExceptionHandler {
private StandardGame game = null;
public ExceptionHandler(StandardGame game) {
this.game = game;
}
public void uncaughtException(Thread t, final Throwable e) {
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ExceptionDialog dialog = new ExceptionDialog("<Your game>", e);
center(dialog);
dialog.setVisible(true);
// Dispose of the dialog to prevent the swing thread
// from keeping the application alive.
dialog.dispose();
}
});
}
catch (Exception ex) {
ex.printStackTrace();
}
// After getting swing started, be sure to shutdown standard game
game.shutdown();
}
}
/**
* Gamestate used to throw an uncaught exception.
*/
public static class ExceptionGameState extends GameState {
@Override
public void cleanup() {
}
@Override
public void render(float tpf) {
}
@Override
public void update(float tpf) {
throw new NullPointerException("This is an uncaught exception.");
}
}
/**
* This dialog will present the details of a throwable exception to a user.
*/
public static class ExceptionDialog extends JDialog {
private static final long serialVersionUID = 1L;
public ExceptionDialog(String appName, final Throwable e) {
final JPanel contentPane = new JPanel();
contentPane.setLayout(new BorderLayout());
contentPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
setContentPane(contentPane);
JPanel messagePanel = new JPanel();
messagePanel.setLayout(new BorderLayout());
contentPane.add(messagePanel, BorderLayout.PAGE_START);
messagePanel.add(new JLabel(appName + " has been terminated due to an unrecoverable error."), BorderLayout.PAGE_START);
JLabel errorType = new JLabel(e.getClass().getSimpleName());
errorType.setFont(errorType.getFont().deriveFont(Font.PLAIN));
messagePanel.add(errorType, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT, 10, 0));
JButton close = new JButton("Close");
close.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
buttonPanel.add(close);
final JButton details = new JButton("Details");
details.addActionListener(new ActionListener() {
/**
* When the user clicks the details button, add the stack trace
* to the dialog and disable the details button.
*/
public void actionPerformed(ActionEvent evt) {
JLabel detailsLabel = new JLabel("Error Details");
detailsPanel.add(detailsLabel, BorderLayout.PAGE_START);
/*
* This special construction of a JTextPane prevents line
* wrapping, allowing the scrollpane to do its job
* horizontally.
*/
final JTextPane detailsText = new JTextPane() {
private static final long serialVersionUID = 1L;
public boolean getScrollableTracksViewportWidth() {
return (getSize().width < getParent().getSize().width);
}
detailsText.setEditable(false);
/* Uncomment this line if you don't like the white background of the text pane */
//detailsText.setBackground(contentPane.getBackground());
// Print the stack trace to a string...
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
e.printStackTrace(pw);
pw.flush();
sw.flush();
detailsText.setText(sw.toString());
JScrollPane scrollPane = new JScrollPane(detailsText);
I actually got it working by try-catching the entire update/render loop since I read somewhere that the application is "already closing" when the uncaughtExceptionHandler sets in. Whatever that means.
I like your solution better, naturally, and will try it out. The whole panel on the pane thing was shamelessly stolen from somewhere.