Search code examples
javaswingfocusjcomboboxjeditorpane

How to edit a JComboBox with text selected in JEditorPane


I have a UI with two components - a JEditorPane and a JComboBox. My goal is to be able to type something into the JEditorPane, select a portion of the text, and while it is still selected type and/or select a value in an editable JComboBox.

This is for a text editor type of program where I want to change the font size of just the selected text in the editor pane. Where the font size is coming from the editable combo box. To clarify, I'm not asking how to apply styles to the text, I'm asking how to select a value in the combo box without losing the focus/selection in the JEditorPane.

Here's the code for the UI, but I wasn't sure where to begin doing anything with the focus...

public static void main(String [] args)
{
    JFrame frame = new JFrame();
    JPanel contentPane = new JPanel();

    JComboBox<String> combo = new JComboBox(new String [] {"Hello", "World"});
    contentPane.add(combo);

    JEditorPane editor = new JEditorPane();
    contentPane.add(editor);

    frame.setContentPane(contentPane);
    frame.pack();
    frame.setVisible(true);
}

Solution

  • I'm asking how to select a value in the combo box without losing the focus/selection in the JEditorPane.

    You don't lose the selection of the text in the editor pane when you select an item from the combo box. The selection remains, but it is just not painted until the editor pane regains focus.

    So the easiest way to do this is to use a JMenuItem. Read the section from the Swing tutorial on Text Component Features for an example that does this.

    If you still want to use the combo box then you can add Integer values to the combo box then the code in your ActionListener for the combo box would look something like:

    @Override
    public void actionPerformed(ActionEvent e)
    {
        Integer value = (Integer)comboBox.getSelectedItem();
        Action action = new StyledEditorKit.FontSizeAction("Font size", value);
        action.actionPerformed(null);
    }
    

    The StyledEditorKit actions extend from TextAction. The TextAction knows the last text component that had focus and therefore the font change is applied to that text component.

    If you really want the text field to show the selection then you need to create a custom Caret and override the focusLost method to NOT invoke setSelectionVisible(false) (which is the default behaviour.

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import javax.swing.text.*;
    
    public class DefaultCaretTest extends JFrame
    {
        public DefaultCaretTest()
        {
            JTextField textField1 = new JTextField("Text Field1   ");
            JTextField textField2 = new JTextField("Text Field2   ");
    
            textField1.setCaret(new SelectionCaret());
            textField2.setCaret(new SelectionCaret());
    
            textField1.select(5, 11);
            textField2.select(5, 11);
            ((DefaultCaret)textField2.getCaret()).setSelectionVisible(true);
    
            add(textField1, BorderLayout.WEST);
            add(textField2, BorderLayout.EAST);
        }
    
        static class SelectionCaret extends DefaultCaret
        {
            public SelectionCaret()
            {
                setBlinkRate( UIManager.getInt("TextField.caretBlinkRate") );
            }
    
            public void focusGained(FocusEvent e)
            {
                setVisible(true);
                setSelectionVisible(true);
            }
    
            public void focusLost(FocusEvent e)
            {
                setVisible(false);
            }
        }
    
        public static void main(String[] args)
        {
            DefaultCaretTest frame = new DefaultCaretTest();
            frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
            frame.pack();
            frame.setLocationRelativeTo( null );
            frame.setVisible(true);
        }
    }
    

    Of course the selection will remain when focus is on any other component, not just the combo box.

    You can also use:

    comboBox.setFocusable(false);
    

    Since the combo box can't gain focus the focus will remain on the text component, but the problem with this is that the user won't be able to use the keyboard to select a font size from the combo box. A proper GUI design always allows the user to use either the keyboard or the mouse to perform an action.