JInternalFrame As Modal

2019-05-29 05:22发布

I Have the following code:

import java.awt.AWTEvent;
import java.awt.ActiveEvent;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.MenuComponent;
import java.awt.event.MouseEvent;
import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;

public class modalInternalFrame extends JInternalFrame {

// indica si aquest es modal o no.
    boolean modal = false;

    @Override
    public void show() {
        super.show();
        if (this.modal) {
            startModal();
        }
    }

    @Override
    public void setVisible(boolean value) {
        super.setVisible(value);
        if (modal) {
            if (value) {
                startModal();
            } else {
                stopModal();
            }
        }
    }

    private synchronized void startModal() {

        try {
            if (SwingUtilities.isEventDispatchThread()) {
                EventQueue theQueue =
                        getToolkit().getSystemEventQueue();
                while (isVisible()) {
                    AWTEvent event = theQueue.getNextEvent();
                    Object source = event.getSource();
                    boolean dispatch = true;

                    if (event instanceof MouseEvent) {
                        MouseEvent e = (MouseEvent) event;
                        MouseEvent m =
                                SwingUtilities.convertMouseEvent((Component) e.getSource(), e, this);
                        if (!this.contains(m.getPoint()) && e.getID() != MouseEvent.MOUSE_DRAGGED) {
                            dispatch = false;
                        }
                    }

                    if (dispatch) {
                        if (event instanceof ActiveEvent) {
                            ((ActiveEvent) event).dispatch();
                        } else if (source instanceof Component) {
                            ((Component) source).dispatchEvent(
                                    event);
                        } else if (source instanceof MenuComponent) {
                            ((MenuComponent) source).dispatchEvent(
                                    event);
                        } else {
                            System.err.println(
                                    "Unable to dispatch: " + event);
                        }
                    }
                }
            } else {
                while (isVisible()) {
                    wait();
                }
            }
        } catch (InterruptedException ignored) {
        }

    }

    private synchronized void stopModal() {
        notifyAll();
    }

    public void setModal(boolean modal) {
        this.modal = modal;
    }

    public boolean isModal() {
        return this.modal;
    }
}

Then I used the NetBeans GUI to draw my JInternalFrame, but just changed the code in the class declaration to extend modalInternalFrame instead of JInternalFrame:

public class myDialog extends modalInternalFrame { 
....

and then used this to actually display it from my top-level "desktop" JFrame (containing jDesktopPane1):

myDialog d = new myDialog();
d.setModal(true);
d.setBounds(160, 180, 550, 450);
jDesktopPane1.add(d);
d.setVisible(true);

My Problem is: If the internal frame has JComboBox or PopupMenu, when part of PopupMenu is out of the internal frame's boundry that part don't handle mouse event (you cann't scroll that part).

Any Ideas?

2条回答
Emotional °昔
2楼-- · 2019-05-29 05:54

How about using the JOptionPane.showInternalMessageDialog(...):

  • I am running JDK 1.7.0_21 on Windows 7 x64:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class ModalInternalFrameTest {
  private final JDesktopPane desktop = new JDesktopPane();
  private final String[] items = new String[] {
    "bananas", "pizza", "hot dogs", "ravioli"
  };
  private final Action openAction = new AbstractAction("open") {
    @Override public void actionPerformed(ActionEvent e) {
      JComboBox<String> combo = new JComboBox<String>(items);
      combo.setEditable(true);
      JOptionPane.showInternalMessageDialog(desktop, combo);
      System.out.println(combo.getSelectedItem());
    }
  };
  public JComponent makeUI(JFrame frame) {
    frame.setJMenuBar(createMenuBar());

    JButton button = new JButton(openAction);
    button.setMnemonic(KeyEvent.VK_S);
    JInternalFrame internal = new JInternalFrame("Button");
    internal.getContentPane().add(button);
    internal.setBounds(20, 20, 100, 100);
    desktop.add(internal);
    internal.setVisible(true);

    JButton b = new JButton(new AbstractAction("beep") {
      @Override public void actionPerformed(ActionEvent e) {
        Toolkit.getDefaultToolkit().beep();
      }
    });
    b.setMnemonic(KeyEvent.VK_B);

    JPanel p = new JPanel(new BorderLayout());
    p.add(b, BorderLayout.SOUTH);
    p.add(desktop);
    return p;
  }
  private JMenuBar createMenuBar() {
    JMenuBar menuBar = new JMenuBar();
    JMenu menu = new JMenu("Frame");
    menu.setMnemonic(KeyEvent.VK_F);
    menuBar.add(menu);

    JMenuItem menuItem = new JMenuItem(openAction);
    menuItem.setMnemonic(KeyEvent.VK_1);
    menuItem.setAccelerator(
      KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK));
    menu.add(menuItem);

    return menuBar;
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();
      }
    });
  }
  public static void createAndShowGUI() {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.getContentPane().add(new ModalInternalFrameTest().makeUI(f));
    f.setSize(640, 480);
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  }
}
查看更多
Evening l夕情丶
3楼-- · 2019-05-29 06:12

There are three types of popups:

  • light weight
  • medium weight
  • heavy weight

The only one that works in a modal state is the heavy weight popup. The "offical" way to change the popup's weight is through the setLightWeightPopupEnabled(boolean aFlag) method. If you set it false the popup will be medium weight when it's inside the application frame and heavy weight when exceeds the frame bounds.

To force heavy weight, you have to use a client property called javax.swing.ClientPropertyKey.PopupFactory_FORCE_HEAVYWEIGHT_POPUP. With this property you can force popups (or every popup in a container) to be heavy weight. But the only way to access it is through reflection because it's private.

Here is an example code:

try {
  Class<?> enumElement = Class.forName("javax.swing.ClientPropertyKey");
  Object[] constants = enumElement.getEnumConstants();
  putClientProperty(constants[3], Boolean.TRUE);
}
catch(ClassNotFoundException ex) {}

If you put this inside your modalInternalFrame's contructor, then every popup placed on it will be heavy weight.

查看更多
登录 后发表回答