[英]Adding a context menu to all Swing text components in application
Swing文本組件沒有帶有cut / copy / paste / etc的上下文菜單。 我想添加一個,使其表現得更流暢,就像一個本機應用程序。 我為此寫了一個菜單,它工作正常。 我使用以下命令將其添加到每個文本框中:
someTextBox.setComponentPopupMenu(TextContextMenu.INSTANCE);
關鍵是,將其添加到任何地方都是令人討厭的。 其次,如果我忘記了某個地方的文本框,則該應用程序會不一致。 第三,我無法將其添加到無法控制創建代碼的文本框中,例如JOptionPane.showInputDialog
或JFileChooser
對話框中的文本框。
有什么方法可以覆蓋應用程序范圍內JTextComponent
的默認上下文菜單嗎? 我知道這是一種遙不可及的怪異動作,但是我可以接受。 也歡迎對菜單本身進行評論。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.JTextComponent;
public class TextContextMenu extends JPopupMenu implements ActionListener {
public static final TextContextMenu INSTANCE = new TextContextMenu();
private final JMenuItem itemCut;
private final JMenuItem itemCopy;
private final JMenuItem itemPaste;
private final JMenuItem itemDelete;
private final JMenuItem itemSelectAll;
private TextContextMenu() {
itemCut = newItem("Cut", 'T');
itemCopy = newItem("Copy", 'C');
itemPaste = newItem("Paste", 'P');
itemDelete = newItem("Delete", 'D');
addSeparator();
itemSelectAll = newItem("Select All", 'A');
}
private JMenuItem newItem(String text, char mnemonic) {
JMenuItem item = new JMenuItem(text, mnemonic);
item.addActionListener(this);
return add(item);
}
@Override
public void show(Component invoker, int x, int y) {
JTextComponent tc = (JTextComponent)invoker;
boolean changeable = tc.isEditable() && tc.isEnabled();
itemCut.setVisible(changeable);
itemPaste.setVisible(changeable);
itemDelete.setVisible(changeable);
super.show(invoker, x, y);
}
@Override
public void actionPerformed(ActionEvent e) {
JTextComponent tc = (JTextComponent)getInvoker();
tc.requestFocus();
boolean haveSelection = tc.getSelectionStart() != tc.getSelectionEnd();
if (e.getSource() == itemCut) {
if (!haveSelection) tc.selectAll();
tc.cut();
} else if (e.getSource() == itemCopy) {
if (!haveSelection) tc.selectAll();
tc.copy();
} else if (e.getSource() == itemPaste) {
tc.paste();
} else if (e.getSource() == itemDelete) {
if (!haveSelection) tc.selectAll();
tc.replaceSelection("");
} else if (e.getSource() == itemSelectAll) {
tc.selectAll();
}
}
}
我已經弄清楚了如何在整個應用程序中執行此操作,包括在JFileChoosers和showInputDialog之類的東西上! 我不確定這是多么的理智和適當,但它可以工作。 (ab)使用可插入的外觀系統。 JTextComponent在其構造函數期間調用updateUI,這提供了在L&F被要求為其UI委托時調用setComponentPopupMenu的機會。
如果更改已經打開的窗口的外觀,則將再次調用每個組件的updateUI方法。 為了防止再次設置默認菜單,下面的代碼存儲了是否使用JComponent.putClientProperty初始化了文本框的屬性。
最終結果是,它的行為就像每個JTextComponent本身在其構造函數期間僅調用一次setComponentPopupMenu一樣。 因此,對於不需要菜單或需要其他菜單的特殊文本框,可以輕松覆蓋此設置:只需再次調用setComponentPopupMenu。 例如,來自文本字段子類構造函數或來自創建窗口及其小部件的調用代碼。
這是在應用程序啟動時運行一次的代碼:
UIManager.addAuxiliaryLookAndFeel(new LookAndFeel() {
private final UIDefaults defaults = new UIDefaults() {
@Override
public javax.swing.plaf.ComponentUI getUI(JComponent c) {
if (c instanceof javax.swing.text.JTextComponent) {
if (c.getClientProperty(this) == null) {
c.setComponentPopupMenu(TextContextMenu.INSTANCE);
c.putClientProperty(this, Boolean.TRUE);
}
}
return null;
}
};
@Override public UIDefaults getDefaults() { return defaults; };
@Override public String getID() { return "TextContextMenu"; }
@Override public String getName() { return getID(); }
@Override public String getDescription() { return getID(); }
@Override public boolean isNativeLookAndFeel() { return false; }
@Override public boolean isSupportedLookAndFeel() { return true; }
});
您有兩種選擇:
如果您的應用程序具有自己的自定義外觀,則只需在TextUI的installUI(JComponent c)
方法中進行即可。
如果沒有自己的L&F,則必須遍歷Component樹,找到JTextComponent的所有實例,然后添加上下文菜單。 我會將其作為靜態輔助方法。 對於對話框和框架,您需要一個基本類,該類重新定義了setVisible()方法,以為所有文本組件添加上下文菜單。 您所有的自定義對話框或框架都必須擴展基本對話框/框架。
由於與JavaHelp發生沖突,因此我需要一個不同的解決方案。 我想到的是選擇2的變體。在第二個答案中,我在實例化類時而不是在調用setVisible()
。
public static void installDefaultTextContextMenus( Container aContainer )
{
if ( aContainer != null )
{
if ( aContainer instanceof JFrame )
{
aContainer = ((JFrame)aContainer).getContentPane();
}
else if ( aContainer instanceof JDialog )
{
aContainer = ((JDialog)aContainer).getContentPane();
}
Component[] lComponents = aContainer.getComponents();
for ( int lCompNum = 0; lCompNum < lComponents.length; ++lCompNum )
{
lComponents[ lCompNum ].getClass();
if ( ( lComponents[ lCompNum ] instanceof JPanel ) ||
( lComponents[ lCompNum ] instanceof JInternalFrame ) ||
( lComponents[ lCompNum ] instanceof JScrollPane ) ||
( lComponents[ lCompNum ] instanceof JSplitPane ) ||
( lComponents[ lCompNum ] instanceof JTabbedPane ) ||
( lComponents[ lCompNum ] instanceof Panel ) ||
( lComponents[ lCompNum ] instanceof ScrollPane ) ||
( lComponents[ lCompNum ] instanceof JViewport ) ||
( lComponents[ lCompNum ] instanceof JFrame ) ||
( lComponents[ lCompNum ] instanceof JDialog ) )
{
installDefaultTextContextMenus( (Container)lComponents[ lCompNum ] );
}
else if ( lComponents[ lCompNum ] instanceof JTextComponent )
{
((JTextComponent)lComponents[ lCompNum ]).setComponentPopupMenu( TextContextMenu.INSTANCE );
}
else if ( lComponents[ lCompNum ] instanceof JComboBox )
{
Component lEditorComp = ((JComboBox)lComponents[ lCompNum ]).getEditor().getEditorComponent();
if ( lEditorComp instanceof JTextComponent )
{
((JTextComponent)lEditorComp).setComponentPopupMenu( TextContextMenu.INSTANCE );
}
}
}
}
}
該方法幾乎可以遍歷任何Swing組件樹。 幸運的是,我已經有了一個用於裝飾應用程序所有頂級JFrame的通用方法,可以向該方法添加調用。 但是,對於沒有由我的頂層菜單處理的框架或對話框的每個實例化,我確實必須向此方法添加一個調用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.