簡體   English   中英

向應用程序中的所有Swing文本組件添加上下文菜單

[英]Adding a context menu to all Swing text components in application

Swing文本組件沒有帶有cut / copy / paste / etc的上下文菜單。 我想添加一個,使其表現得更流暢,就像一個本機應用程序。 我為此寫了一個菜單,它工作正常。 我使用以下命令將其添加到每個文本框中:

someTextBox.setComponentPopupMenu(TextContextMenu.INSTANCE);

關鍵是,將其添加到任何地方都是令人討厭的。 其次,如果我忘記了某個地方的文本框,則該應用程序會不一致。 第三,我無法將其添加到無法控制創建代碼的文本框中,例如JOptionPane.showInputDialogJFileChooser對話框中的文本框。

有什么方法可以覆蓋應用程序范圍內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; }
});

您有兩種選擇:

  1. 如果您的應用程序具有自己的自定義外觀,則只需在TextUI的installUI(JComponent c)方法中進行即可。

  2. 如果沒有自己的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM