Swing/ModalInternalFrame のバックアップの現在との差分(No.10)
TITLE:JInternalFrameをModalにする
Posted by aterai at 2007-10-15
JInternalFrameをModalにする
JInternalFrameをModalにして、他のJInternalFrameなどを操作できないようにブロックします。-
category: swing
folder: ModalInternalFrame
title: JInternalFrameをModalにする
tags: [JInternalFrame, GlassPane, Mnemonic, JDesktopPane, JToolTip, JLayeredPane]
author: aterai
pubdate: 2007-10-15T13:16:07+09:00
description: JInternalFrameをModalにして、他のJInternalFrameなどを操作できないようにブロックします。
image:
hreflang:
href: https://java-swing-tips.blogspot.com/2008/10/modal-internal-frame.html lang: en
概要
JInternalFrame
をModal
にして、他のJInternalFrame
などを操作できないようにブロックします。
- &jnlp;
- &jar;
- &zip;
Screenshot
Advertisement
サンプルコード
#spanend
#spandel
//menuItem.setMnemonic(KeyEvent.VK_1);
#spanend
#spanadd
* サンプルコード [#sourcecode]
#spanend
#spanadd
#code(link){{
#spanend
#spanadd
// menuItem.setMnemonic(KeyEvent.VK_1);
#spanend
class ModalInternalFrameAction1 extends AbstractAction {
public ModalInternalFrameAction1(String label) {
super(label);
}
public void actionPerformed(ActionEvent e) {
#spanadd
#spanend
@Override public void actionPerformed(ActionEvent e) {
setJMenuEnabled(false);
JOptionPane.showInternalMessageDialog(
desktop, "information", "modal1", JOptionPane.INFORMATION_MESSAGE);
setJMenuEnabled(true);
}
}
#spandel
#spanend
#spandel
//menuItem.setMnemonic(KeyEvent.VK_2);
#spanend
#spanadd
#spanend
#spanadd
// menuItem.setMnemonic(KeyEvent.VK_2);
#spanend
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.setLocation(0, 0);
glass.setSize(screen.width, screen.height);
glass.setOpaque(false);
glass.setVisible(false);
desktop.add(glass, JLayeredPane.MODAL_LAYER);
}
public void actionPerformed(ActionEvent e) {
#spanadd
#spanend
@Override public void actionPerformed(ActionEvent e) {
setJMenuEnabled(false);
glass.setVisible(true);
JOptionPane.showInternalMessageDialog(
desktop, "information", "modal2", JOptionPane.INFORMATION_MESSAGE);
glass.setVisible(false);
setJMenuEnabled(true);
}
}
#spandel
#spanend
#spandel
//menuItem.setMnemonic(KeyEvent.VK_3);
#spanend
#spandel
//Creating Modal Internal Frames -- Approach 1 and Approach 2
#spanend
#spandel
//http://java.sun.com/developer/JDCTechTips/2001/tt1220.html
#spanend
#spanadd
#spanend
#spanadd
// menuItem.setMnemonic(KeyEvent.VK_3);
#spanend
#spanadd
// Creating Modal Internal Frames -- Approach 1 and Approach 2
#spanend
#spanadd
// http://web.archive.org/web/20090803142839/http://java.sun.com/developer/JDCTechTips/2001/tt1220.html
#spanend
class ModalInternalFrameAction3 extends AbstractAction {
private final Component orgGlassPane;
private final JPanel glass = new PrintGlassPane();
public ModalInternalFrameAction3(String label) {
super(label);
orgGlassPane = frame.getGlassPane();
glass.setVisible(false);
}
public void actionPerformed(ActionEvent e) {
#spanadd
#spanend
@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);
JInternalFrame modal = optionPane.createInternalFrame(desktop, "modal3");
removeSystemMenuListener(modal);
modal.addInternalFrameListener(new InternalFrameAdapter() {
public void internalFrameClosed(InternalFrameEvent e) {
@Override public void internalFrameClosed(InternalFrameEvent e) {
glass.removeAll();
glass.setVisible(false);
frame.setGlassPane(orgGlassPane);
}
});
glass.add(modal);
Rectangle screen = desktop.getBounds();
modal.setLocation(screen.x + screen.width/2 - modal.getSize().width/2,
screen.y + screen.height/2 - modal.getSize().height/2);
frame.setGlassPane(glass);
modal.pack();
getRootPane().setGlassPane(glass);
glass.setVisible(true);
modal.setVisible(true);
try{
modal.setSelected(true);
}catch(java.beans.PropertyVetoException ex) {}
}
}
解説
- Alt+1: JOptionPane.showInternalMessageDialogメソッドを使用して、簡単なメッセージを表示するModalなDialogをJDeskTopPane内に表示します。
- Mnemonicがうまく無効に出来ないため、Alt+Bでボタンを押すことが出来てしまいます(Mnemonicを設定したコンポーネントはsetEnabled(false)とする必要がある)。
- MnemonicをJMenuに設定しているとsetEnabled(false)としても、Altキーに反応してしまう(Windows L&Fだけ?)ので、ダイアログを表示している間は、JMenuBarをダミーと入れ替えています。
- このダイアログを閉じない限り、アプリケーションをAlt+F4などで閉じることは出来ません。
- ダイアログのシステムメニュー(左上のアイコンをクリックすると表示される)がマウスで操作できません。
解説
- 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と同様ですが、
もう一枚タイトルバーなどを削除した半透明なJInternalFrameを下の半透明なGlassPaneをJLayeredPane.MODAL_LAYERに追加して表示しています。- JDeskTop内にマスクが掛かります。
- Alt+2: Alt+1と同様に
JOptionPane.showInternalMessageDialog
メソッドを使用し、かつ半透明なGlassPane
をJLayeredPane.MODAL_LAYER
に追加- 動作、制限などはAlt+2の
InternalMessageDialog
と同じ -
JDesktopPane
内にマスクが掛かる
- 動作、制限などはAlt+2の
- Alt+3: JFrameに半透明なGlassPaneを追加し、そこにJInternalFrameを追加することでModalにしています。
- JFrame内全体(JMenuBarなども含む)にマスクが掛かります。
-
ダイアログのシステムメニューが自身のレイヤーより奥に表示されるため、アイコン(JLabel)をクリックしても反応しないようにリスナーを取り除いています。 - このダイアログを開いていても、アプリケーションをAlt+F4などで閉じることが出来てしまいます。
- Alt+3:
JFrame
に半透明なGlassPane
を追加しそこにJInternalFrame
を追加することでModal
に設定-
JFrame
内全体(JMenuBar
なども含む)にマスクが掛かる -
InternalMessageDialog
のシステムメニューが自身のレイヤーより奥に表示されるため、アイコン(JLabel
)をクリックしても反応しないようにリスナーを除去 -
JComboBox
をInternalMessageDialog
に追加するとそのドロップダウンメニューが裏に表示される - この
InternalMessageDialog
を開いていてもアプリケーションをAlt+F4などで閉じることが出来てしまう
-
Alexander Potochkin's Blog: Disabling Swing Containers, the final solution?を参考に*1して、GlassPane を以下のように修正すると、上記のサンプルのAlt+3*2は、Mnemonic もうまくブロックできるようです。
- JFrameのメニューバーのMnemonicもブロックできる
- JFrameのシステムメニューはブロックできない
- モーダルにしたJInternalFrameのシステムメニューは表示されない
- ただし表示されないだけで、クリックしてからカーソル移動やダブルクリックなどが動いてしまう
- モーダルにしたJInternalFrameの右上の閉じるボタンのJToolTip がJDesktopPane内で表示される場合、空白になる
JDK 6 ではJToolTipは表示されない - Alt+3の方法で、
InternalOptionDialog
にJComboBox
を追加する場合、ドロップダウンメニューを正しく表示させるにはリフレクションを使ってClientProperty
を設定するしかない?-
JInternalFrame#putClientProperty(PopupFactory_FORCE_HEAVYWEIGHT_POPUP, Boolean.TRUE)
とすればシステムメニューも正常に表示されるが、JOptionPane.showInternalXXXDialog
ではなぜかJOptionPane
に設定するようになっている(JInternalFrame
は使い回ししているから?)
-
#spanadd
JInternalFrame modal = optionPane.createInternalFrame(desktop, "modal3");
#spanend
#spanadd
JComboBox combo = new JComboBox(new String[] {"Banana", "Apple", "Pear", "Grape"});
#spanend
#spanadd
combo.setEditable(true);
#spanend
#spanadd
try {
#spanend
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);
#spanadd
} catch (Exception ex) {
#spanend
ex.printStackTrace();
#spanadd
}
#spanend
#spanadd
optionPane.setMessage(combo);
#spanend
#spanadd
optionPane.setMessageType(JOptionPane.QUESTION_MESSAGE);
#spanend
#spanadd
- -
- 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);
で非表示になる
-
#spanend
class PrintGlassPane extends JPanel {
TexturePaint texture = TextureFactory.createCheckerTexture(4);
public PrintGlassPane() {
super((LayoutManager)null);
super((LayoutManager) null);
setOpaque(false);
}
@Override
public void setVisible(boolean isVisible) {
#spanadd
#spanend
@Override public void setVisible(boolean isVisible) {
boolean oldVisible = isVisible();
super.setVisible(isVisible);
JRootPane rootPane = SwingUtilities.getRootPane(this);
if(rootPane!=null && isVisible()!=oldVisible) {
if (rootPane != null && isVisible() != oldVisible) {
rootPane.getLayeredPane().setVisible(!isVisible);
}
}
@Override
public void paintComponent(Graphics g) {
#spanadd
#spanend
@Override protected void paintComponent(Graphics g) {
JRootPane rootPane = SwingUtilities.getRootPane(this);
if(rootPane!=null) {
//http://weblogs.java.net/blog/alexfromsun/archive/2008/01/
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());
g2.fillRect(0, 0, getWidth(), getHeight());
}
}
JDK 6 の場合、Disabled Glass Pane « Java Tips Weblogのようにキー入力を無効にするキーリスナーを追加する方法もあります。
-
JDK 6
の場合、Disabled Glass Pane « Java Tips Weblogのようにキー入力を無効にするキーリスナーを追加する方法もある-
JDK 5
などの場合、WindowsLookAndFeel
でAltキーを押すとメニューバーにフォーカスが移動してしまう
-
参考リンク
- 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?
参考リンク
- Creating Modal Internal Frames -- Approach 1 and Approach 2
- How to Use Root Panes
- Disabling Swing Containers, the final solution?
コメント
-
JInternalFrameを半透明にすると、同様に-- ateraiGlassPane
がUbuntu
(GNOME
)などで半透明にならない場合があります。- Alt+2で開いた場合、
JInternalFrame
にGlassPane
を乗せるのではなく、直接JDesktopPane
のJLayeredPane.MODAL_LAYER
に追加するように変更しました。 -- aterai
- Alt+2で開いた場合、
- メモ: Alexander Potochkin's Blog: Disabling Swing Containers, the final solution?のサンプルでは、
Mnemonic
もちゃんとブロックできているようなので、「あとで調べる & 参考にする」こと。 -- aterai -
Mnemonic
を数字キー(1, 2, 3)に変更 -- aterai - すべての
Mnemonic
を一時的に無効化したい場合に、UIManager.java
のprivate static final String disableMnemonicKey = "swing.disablenavaids";
は使えない? 以下のようにKeyboardFocusManager.setCurrentKeyboardFocusManager(...)
を使用してAltキーなどを無視することも可能だが、もっと簡単な方法を調査中。 -- aterai#spanend #spanadd KeyboardFocusManager.setCurrentKeyboardFocusManager(new DefaultKeyboardFocusManager() { #spanend @Override public boolean dispatchKeyEvent(KeyEvent e) { if ((e.getModifiersEx() & InputEvent.ALT_DOWN_MASK) != 0) { // System.out.println(e); return false; } return super.dispatchKeyEvent(e); } #spanadd }); #spanend #spanadd
コメント
-
JInternalFrameを半透明にすると、同様にGlassPaneがUbuntu(GNOME)などで半透明にならない場合があります。-- aterai- (Alt+2)で開いた場合、JInternalFrameにGlassPaneを乗せるのではなく、直接JDeskTopPaneのJLayeredPane.MODAL_LAYERに追加するように変更しました。 -- aterai
- メモ: Alexander Potochkin's Blog: Disabling Swing Containers, the final solution?のサンプルでは、Mnemonic もちゃんとブロックできているようなので、「あとで調べる & 参考にする」こと。 -- aterai
- Mnemonic を数字(1-3)に変更 -- aterai
- メモ: JDK-6921687 Mnemonic disappears after repeated attempts to open menu items using mnemonics - Java Bug System -- aterai