This discussion is archived
5 Replies Latest reply: Mar 28, 2013 5:52 PM by Maxideon RSS

Custom modal internal dialogs possible with Swing?

user12866708 Newbie
Currently Being Moderated
Hi,

Is there any way to create JInternalFrames which behave lilke a JDialog (modal, own event loop, block main event thread)?

JOptionPane offers quite a few ways to create pre-defined internal modal dialogs which do exactly that (showInternalXYZDialog). Therefore the whole apparatus required to support JInternalFrame based dialogs seems to be there, yet I was not able to find any API which allows to do the same with custom dialogs.

It would be great if somebody could answer my two questions:

1. Are there any workarrounds or even 3rd party libraries which would allow to create custom modal, internal dialogs?
2. Is there any chance to get an appropriate API into JDK8 as part of OpenJDK? Or is swing considered API-frozen?

Thank you in advance, Clemens
  • 1. Re: Custom modal internal dialogs possible with Swing?
    jduprez Pro
    Currently Being Moderated
    From the Swing tutorial chapter on Internal frames (http://docs.oracle.com/javase/tutorial/uiswing/components/internalframe.html):
    Dialogs that are internal frames should be implemented using JOptionPane or JInternalFrame, not JDialog.
    The tutorial chapter on dialogs (http://docs.oracle.com/javase/tutorial/uiswing/components/dialog.html) describes how to use either of the static JOptionPane.showInternalXxxDialog methods.
    But it also mentions that you can create an instances of JOptionPane right away, and show it in a JInternalFrame using createInternalFrame (http://docs.oracle.com/javase/7/docs/api/javax/swing/JOptionPane.html#createInternalFrame%28java.awt.Component,%20java.lang.String%29)

    Brgds,

    J.
  • 2. Re: Custom modal internal dialogs possible with Swing?
    user12866708 Newbie
    Currently Being Moderated
    Unfourtunately this doesn't help, as only the static showInternalXYZDialog-Methods implement modal behaviour.
    Isn't it possible at all to create a modal JInternalFrame with custom content?

    Thank you in advance, Clemens
  • 3. Re: Custom modal internal dialogs possible with Swing?
    Maxideon Explorer
    Currently Being Moderated
    You can imitate the effects of modality. Wrap your JLayerPane in a JLayer. Override the LayerUI#eventDispatched method to consume() any event not related to the dialog (when present).
  • 4. Re: Custom modal internal dialogs possible with Swing?
    user12866708 Newbie
    Currently Being Moderated
    What I would need is not only modality, but also the blocking behaviour of a JDialog which effectively halts execution of the EDT until the Dialog is disposed.

    I've found a version which does some of this but doesn't work very well. I am working on a more useful implementation and will put it online somewhere soon.

    Regards
  • 5. Re: Custom modal internal dialogs possible with Swing?
    Maxideon Explorer
    Currently Being Moderated
    What I would need is not only modality, but also the blocking behaviour of a JDialog which effectively halts execution of the EDT until the Dialog is disposed.
    That can be done too. Use EventQueue#createSecondaryLoop to block the currently executing EDT while spawning another one.

    Edit
     import javax.swing.*;
     import javax.swing.plaf.LayerUI;
     import java.awt.*;
     import java.awt.event.*;
    
     public class InternalDialogTest {
        public static void main(String[] args) throws Exception {
             SwingUtilities.invokeAndWait(new Runnable() {
                 public void run() {
                     createAndShowGUI();
                 }
             });
         }
        private static void createAndShowGUI() {
             JFrame frame = new JFrame();
             frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
             frame.add(createLayer());
             frame.setSize(256, 256);
             frame.setLocationRelativeTo(null);
             frame.setVisible(true);
         } 
         private static JLayer<JLayeredPane> createLayer() {
             final ModalLayerUI layerUI = new ModalLayerUI();
             final JLayeredPane layeredPane = new JLayeredPane();
             JLayer<JLayeredPane> layer = new JLayer<>(layeredPane, layerUI);
             
             JInternalFrame internal = new JInternalFrame("Internal Frame");
             internal.setResizable(true);
             
             JButton button = new JButton("Make dialog");
             button.addActionListener(new ActionListener() {
                 public void actionPerformed(ActionEvent e) {
                     JInternalFrame cd = new JInternalFrame("Custom Dialog");
                     cd.add(new JLabel("Internal Custom Dialog"));
                     cd.setClosable(true);
                     cd.pack();
                     cd.setLocation((layeredPane.getWidth()-cd.getWidth())/2,
                                    (layeredPane.getHeight()-cd.getHeight())/2);
                    
                     System.out.println("Showing Dialog");
                     layerUI.showModalDialog(layeredPane, cd);
                     System.out.println("Done showing Dialog");
                 }
             });
             
             internal.add(button);
             internal.pack(); 
             layeredPane.add(internal);
             internal.setVisible(true);
                     
             return layer;
         }
         public static class ModalLayerUI extends LayerUI<JLayeredPane> 
                implements Runnable {
          
           JInternalFrame dialog;
           SecondaryLoop blockingLoop;
           
           /*The JLayeredPane's parent is assumed to be the JLayer containing this
            *this LayerUI.  The dialog needs to be packed, layed out, and ready 
            *to be made visible prior to calling this method.  This method blocks, 
            *until the dialog has been closed.
            */
           public void showModalDialog(JLayeredPane pane, JInternalFrame dialog) {
               if(this.dialog != null) {
                   throw new IllegalStateException("Modal dialog already present!");
               }
               
               this.dialog = dialog;
               
               Component prevFocusOwner = FocusManager.getCurrentManager().getFocusOwner();
               
               pane.add(dialog,JLayeredPane.MODAL_LAYER);
               dialog.setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE);
               dialog.setVisible(true);
               dialog.requestFocusInWindow();
               
               EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
               blockingLoop = queue.createSecondaryLoop();
               
               ((JLayer) pane.getParent()).setLayerEventMask(-1);
               if(!blockingLoop.enter()) {
                   throw new IllegalStateException("SecondaryLoop already in use");
               }
               ((JLayer) pane.getParent()).setLayerEventMask(0);
               prevFocusOwner.requestFocusInWindow();
           }
           public void eventDispatched(AWTEvent e, JLayer<? extends JLayeredPane> l) {
               if(dialog == null) 
                   return;
               
               Object source = e.getSource();
               
               if(source instanceof Component && 
                       SwingUtilities.isDescendingFrom((Component) source, dialog)){
                   SwingUtilities.invokeLater(this);
               }else {
                   if(e instanceof InputEvent)
                       ((InputEvent) e).consume();
                   else if(e instanceof FocusEvent)
                       dialog.requestFocusInWindow(); //get the focus back     
               }
           }
           public void run() {
               if(dialog != null && !dialog.isVisible()) {
                   dialog = null;
                   blockingLoop.exit();
                   blockingLoop = null;
               }
           }
         }
     }

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points