JInternalFrameをModalにする
Total: 17293, Today: 1, Yesterday: 0
Posted by aterai at
Last-modified:
Summary
JInternalFrameをModalにして、他のJInternalFrameなどを操作できないようにブロックします。
Screenshot

Advertisement
Source Code Examples
// menuItem.setMnemonic(KeyEvent.VK_1);
class ModalInternalFrameAction1 extends AbstractAction {
public ModalInternalFrameAction1(String label) {
super(label);
}
@Override public void actionPerformed(ActionEvent e) {
setJMenuEnabled(false);
JOptionPane.showInternalMessageDialog(
desktop, "information", "modal1", JOptionPane.INFORMATION_MESSAGE);
setJMenuEnabled(true);
}
}
// menuItem.setMnemonic(KeyEvent.VK_2);
class ModalInternalFrameAction2 extends AbstractAction {
private final JPanel glass = new MyGlassPane();
public ModalInternalFrameAction2(String label) {
super(label);
Rectangle screen = frame.getGraphicsConfiguration().getBounds();
glass.setBorder(BorderFactory.createEmptyBorder());
glass.setLocation(0, 0);
glass.setSize(screen.width, screen.height);
glass.setOpaque(false);
glass.setVisible(false);
desktop.add(glass, JLayeredPane.MODAL_LAYER);
}
@Override public void actionPerformed(ActionEvent e) {
setJMenuEnabled(false);
glass.setVisible(true);
JOptionPane.showInternalMessageDialog(
desktop, "information", "modal2", JOptionPane.INFORMATION_MESSAGE);
glass.setVisible(false);
setJMenuEnabled(true);
}
}
// menuItem.setMnemonic(KeyEvent.VK_3);
// Creating Modal Internal Frames -- Approach 1 and Approach 2
// http://web.archive.org/web/20090803142839/http://java.sun.com/developer/JDCTechTips/2001/tt1220.html
class ModalInternalFrameAction3 extends AbstractAction {
private final JPanel glass = new PrintGlassPane();
public ModalInternalFrameAction3(String label) {
super(label);
glass.setVisible(false);
}
@Override public void actionPerformed(ActionEvent e) {
JOptionPane optionPane = new JOptionPane();
JInternalFrame modal = optionPane.createInternalFrame(desktop, "modal3");
optionPane.setMessage("Hello, World");
optionPane.setMessageType(JOptionPane.INFORMATION_MESSAGE);
removeSystemMenuListener(modal);
modal.addInternalFrameListener(new InternalFrameAdapter() {
@Override public void internalFrameClosed(InternalFrameEvent e) {
glass.removeAll();
glass.setVisible(false);
}
});
glass.add(modal);
modal.pack();
getRootPane().setGlassPane(glass);
glass.setVisible(true);
modal.setVisible(true);
}
}
View in GitHub: Java, KotlinDescription
- Alt+1:
JOptionPane.showInternalMessageDialogメソッドを使用して簡単なメッセージを表示するModalなDialogをJDesktopPane内に表示JButtonのマウスクリックは無効になるがMnemonicが無効にならない- Alt+Bでボタンを押すことが出来てしまう
Mnemonicを設定したコンポーネントはsetEnabled(false)とする必要がある
MnemonicをJMenuに設定しているとsetEnabled(false)としてもAltキーに反応する- 追記: JDK-6921687 Mnemonic disappears after repeated attempts to open menu items using mnemonics - Java Bug Systemで修正されそう?
- この
InternalMessageDialogを表示している間はJMenuBarを表示専用の別JMenuBarと入れ替えて無効化
- この
InternalMessageDialogを閉じない限りアプリケーションをAlt+F4などで閉じることは出来ない InternalMessageDialogのシステムメニュー(左上のアイコンをクリックすると表示される)がマウスで操作不可能JToolTipは正常showInternalMessageDialog(...)メソッド内で、pane.putClientProperty(PopupFactory_FORCE_HEAVYWEIGHT_POPUP, Boolean.TRUE)(JDK 1.6.0の場合のKeyはPopupFactory.forceHeavyWeightPopupKey) されているため、JComboBoxなどのドロップダウンメニューも正常
- Alt+2: Alt+1と同様に
JOptionPane.showInternalMessageDialogメソッドを使用し、かつ半透明なGlassPaneをJLayeredPane.MODAL_LAYERに追加- 動作、制限などはAlt+2の
InternalMessageDialogと同じ JDesktopPane内にマスクが掛かる
- 動作、制限などはAlt+2の
- Alt+3:
JFrameに半透明なGlassPaneを追加しそこにJInternalFrameを追加することでModalに設定JFrame内全体(JMenuBarなども含む)にマスクが掛かるInternalMessageDialogのシステムメニューが自身のレイヤーより奥に表示されるため、アイコン(JLabel)をクリックしても反応しないようにリスナーを除去JComboBoxをInternalMessageDialogに追加するとそのドロップダウンメニューが裏に表示される- この
InternalMessageDialogを開いていてもアプリケーションをAlt+F4などで閉じることが出来てしまう
- Alt+3の方法で、
InternalOptionDialogにJComboBoxを追加する場合、ドロップダウンメニューを正しく表示させるにはリフレクションを使ってClientPropertyを設定するしかない?JInternalFrame#putClientProperty(PopupFactory_FORCE_HEAVYWEIGHT_POPUP, Boolean.TRUE)とすればシステムメニューも正常に表示されるが、JOptionPane.showInternalXXXDialogではなぜかJOptionPaneに設定するようになっている(JInternalFrameは使い回ししているから?)
JInternalFrame modal = optionPane.createInternalFrame(desktop, "modal3");
JComboBox combo = new JComboBox(new String[] {"Banana", "Apple", "Pear", "Grape"});
combo.setEditable(true);
try {
Field field;
if (System.getProperty("java.version").startsWith("1.6.0")) {
Class clazz = Class.forName("javax.swing.PopupFactory");
field = clazz.getDeclaredField("forceHeavyWeightPopupKey");
} else { // 1.7.0, 1.8.0
Class clazz = Class.forName("javax.swing.ClientPropertyKey");
field = clazz.getDeclaredField("PopupFactory_FORCE_HEAVYWEIGHT_POPUP");
}
field.setAccessible(true);
modal.putClientProperty(field.get(null), Boolean.TRUE);
} catch (Exception ex) {
ex.printStackTrace();
}
optionPane.setMessage(combo);
optionPane.setMessageType(JOptionPane.QUESTION_MESSAGE);
- Alexander Potochkin's Blog: Disabling Swing Containers, the final solution?を参考に(
paintではなくprintが使用されている)してGlassPaneを以下のように修正すると、上記のサンプルのAlt+3(Alt+2の場合描画が乱れる)はMnemonicもうまくブロックできるようになる JFrameのメニューバーのMnemonicもブロックできるJRootPaneから取得したLayeredPaneが非表示なので、その子コンポーネント(JMenuBarやContentPaneなど)のキーイベントがすべて無効になる- JRootPane (Java Platform SE 8)の図にあるように、
GlassPaneはJRootPaneの最上位の子コンポーネントなのでLayeredPaneを画像として表示している
JFrameのシステムメニューはブロックできない- モーダルにした
JInternalFrameのシステムメニューは表示されない- ただし表示されないだけで、クリックしてからカーソル移動やダブルクリックなどが動いてしまう
- モーダルにした
JInternalFrameの右上の閉じるボタンのJToolTipがJDesktopPane内で表示される場合、空のJToolTipが背面に表示されるUIManager.put("InternalFrame.titleButtonToolTipsOn", Boolean.FALSE);で非表示になる
class PrintGlassPane extends JPanel {
TexturePaint texture = TextureFactory.createCheckerTexture(4);
public PrintGlassPane() {
super((LayoutManager) null);
setOpaque(false);
}
@Override public void setVisible(boolean isVisible) {
boolean oldVisible = isVisible();
super.setVisible(isVisible);
JRootPane rootPane = SwingUtilities.getRootPane(this);
if (rootPane != null && isVisible() != oldVisible) {
rootPane.getLayeredPane().setVisible(!isVisible);
}
}
@Override protected void paintComponent(Graphics g) {
JRootPane rootPane = SwingUtilities.getRootPane(this);
if (rootPane != null) {
// http://weblogs.java.net/blog/alexfromsun/archive/2008/01/
// it is important to call print() instead of paint() here
// because print() doesn't affect the frame's double buffer
rootPane.getLayeredPane().print(g);
}
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(texture);
g2.fillRect(0, 0, getWidth(), getHeight());
}
}
JDK 6の場合、Disabled Glass Pane « Java Tips Weblogのようにキー入力を無効にするキーリスナーを追加する方法もあるJDK 5などの場合、WindowsLookAndFeelでAltキーを押すとメニューバーにフォーカスが移動してしまう
Reference
- JOptionPane#showInternalMessageDialog(...) (Java Platform SE 8)
- Creating Modal Internal Frames -- Approach 1 and Approach 2
- How to Use Root Panes
- Disabling Swing Containers, the final solution?