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);
}
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.
Related
I have a GUI window with several components. Some of them are buttons to which I added keyboard shortcuts. For example, a certain button can be triggered by pressing "a" anywhere in the window. One of the components in a JTextArea. Now, when the focus is in the textarea, and the user types, e.g., "aha" into the JTextArea, the button is triggered twice (in addition to the text "aha" being added to the text area). How can I turn this off? I want the JTextArea to consume the "a" event, so that it does not also trigger the button.
What I want: if an "a" is typed anywhere in the window except in the JTextArea, I want my button to be triggered. But I don't want the button to be triggered while the JTextArea is in focus and the user is typing into the JTextArea.
What I already tried: I tried adding a KeyListener to the JTextArea, which intercepts and consumes any key that is typed. But it had the opposite effect: the button is still triggered, but the letter is not added to the JTextArea.
Here is a minimal example:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
// Create a simple GUI window
public class Gui
{
private static void createWindow()
{
// Create a frame.
JFrame frame = new JFrame("Simple");
// Add a
// Add a text area.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTextArea textarea = new JTextArea();
textarea.setPreferredSize(new Dimension(300, 100));
frame.getContentPane().add(textarea, BorderLayout.CENTER);
// Add a button.
JButton button = new JButton();
button.setText("Button");
button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.get\
KeyStroke(KeyEvent.VK_A, 0), "key");
Action action = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
System.out.println("Action!");
button.doClick();
}
};
button.getActionMap().put("key", action);
frame.getContentPane().add(button, BorderLayout.LINE_END);
frame.pack();
frame.setVisible(true);
}
public static void main(String args[])
{
System.out.println("Hello, World");
createWindow();
}
}
The JTextArea listens for keyTyped events. You are adding a binding for keyPressed.
If you instead create the binding for keyTyped event then the text area will handle the event:
//button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), "key");
button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("typed a"), "key");
I want to construct a Swing component JTextField, here is my Code
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class JTextFieldGui{
JTextField textField;
JLabel labelInput;
JLabel labelOutput;
public static void main(String[] args) {
JTextFieldGui gui = new JTextFieldGui();
gui.go();
}
public void go(){
JFrame frame = new JFrame();
JPanel panelInput = new JPanel();
JPanel panelOutput = new JPanel();
labelInput = new JLabel("Your first name: ");
labelOutput = new JLabel("Enter your name, and you will see it here.");
textField = new JTextField(20);
JButton enter = new JButton("Enter");
JButton selectAll = new JButton("Select all text");
frame.setSize(300,200);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panelInput.setLayout(new BoxLayout(panelInput, BoxLayout.X_AXIS));
textField.addActionListener(new LabelActionListener());
enter.addActionListener(new LabelActionListener());
selectAll.addActionListener(new TextFieldActionlistener());
frame.getContentPane().add(BorderLayout.NORTH, panelInput);
panelInput.add(labelInput);
panelInput.add(textField);
panelInput.add(enter);
panelInput.add(selectAll);
frame.getContentPane().add(BorderLayout.CENTER, panelOutput);
panelOutput.add(labelOutput);
}
class LabelActionListener implements ActionListener{
public void actionPerformed(ActionEvent event){
labelOutput.setText(textField.getText());
}
}
class TextFieldActionlistener implements ActionListener{
public void actionPerformed(ActionEvent event){
textField.selectAll();
}
}
}
Question1: I define the width of the text field in 20 columns, but it always take up a row, like image:
Question2: how to use the selectAll() method, I use it in a listener of the button selectAll, but when I click the button, nothing happens, why
I define the width of the text field in 20 columns, but it always take up a row,
This is the rule of a BoxLayout. A component is resized to fill the space available. A JTextField doesn't have a maximum size so it grows. The buttons and label do have a maximum size so they don't grow.
Don't use a BoxLayout, just use a FlowLayout. It will automatically leave space between each component which is a better layout.
I use it in a listener of the button selectAll, but when I click the button, nothing happens, why
Focus is still on the button. The selected text only displays when the text field has focus.
So in he listener code you need to add:
textField.requestFocusInWindow();
The following code is old:
frame.getContentPane().add(BorderLayout.NORTH, panelInput);
you don't need to get the content pane. You can just add the component to the frame.
the constraint should be the second parameter
there are new constraints to make the names more meaningful
So the code should be:
frame.add(panelInput, BorderLayout.PAGE_START, panelInput);
See the section from the Swing tutorial on How to Use BorderLayout for more information.
I don't want the user to select the content on JTextArea. I use setEditable(false) but it's not working. How to disable this feature of JTextArea component. Could you give me advise. Thanks.
If you would like to just disable text selection on any swing control such as JtextArea you can use the coding below:
JtextArea.setHighlighter(null);
This one line of coding will help disable the text selection and can be placed in the constructor or within a initialized method upon Frame execution.
Hope this helps
You can set the "mark" equal to the "dot" of the caret. When these values are equal there is no text selection:
import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
public class NoTextSelectionCaret extends DefaultCaret
{
public NoTextSelectionCaret(JTextComponent textComponent)
{
setBlinkRate( textComponent.getCaret().getBlinkRate() );
textComponent.setHighlighter( null );
}
#Override
public int getMark()
{
return getDot();
}
private static void createAndShowUI()
{
JTextField textField1 = new JTextField("No Text Selection Allowed");
textField1.setCaret( new NoTextSelectionCaret( textField1 ) );
textField1.setEditable(false);
JTextField textField2 = new JTextField("Text Selection Allowed");
JFrame frame = new JFrame("No Text Selection Caret");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(textField1, BorderLayout.NORTH);
frame.add(textField2, BorderLayout.SOUTH);
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Late to the party, but here are my findings. I had tried using setEnabled(false) on a JTextPane displaying static (not user-modifiable) content such as line numbers (for another text component). This one alone prevents the component from getting focus and text selection on it:
JTextArea textArea = new JTextArea("Static text");
textArea.setEnabled(false);
My problem with setEnabled(false) is that it forces a single disabledTextColor for all of the text (I've traced it down to javax.swing.text.GlyphView.paint()), while I want to style individual lines/chunks. I've finally tried setFocusable(false) that appears to satisfy both needs:
Not user focusable and no user selection on it;
Custom text color could be applied to individual parts of the content, or it just doesn't change the text color to the disabled one.
The complete solution needs additional setEditable(false) to prevent the mouse cursor from changing but that's it – two properties:
JTextArea textArea = new JTextArea("Static text");
textArea.setEditable(false);
textArea.setFocusable(false);
I am trying to change a component from a JLabel to JComboBox when another option is added but for some reason the panel is not updating.
SSCCE:
public class SwitchComponent {
public static void main(String[] args) {
JPanel panel = new JPanel();
JComponent component = new JLabel("This is a test");
panel.add(component);
JComboBox<String> comboBox = new JComboBox<String>();
comboBox.addItem("Testing..");
comboBox.addItem("1.. 2.. 3..");
component = comboBox;
// I have tried with only one of the below lines and without any also...
// Doesn't seem to have an effect.
// I've also tried invoking the below methods on the panel instead.
component.revalidate();
component.repaint();
JOptionPane.showConfirmDialog(null, panel, "Test",
JOptionPane.OK_OPTION,
JOptionPane.PLAIN_MESSAGE);
}
}
Why is this happening? Shouldn't panel be referencing component such that any changes to component are reflected via panel?
Do I really have to completely reassemble the panel when the component changes?
When click YES/NO button on JOptionPane, JOptionPane will close.
We need to add the JComboBox to Panel again and use JOptionPane to show the Panel again in your code.
Have a try with this:
public class SwitchComponent {
public static void main(String[] args) {
JPanel panel = new JPanel();
JComponent component = new JLabel("This is a test");
panel.add(component);
JOptionPane.showConfirmDialog(null, panel, "Test",
JOptionPane.OK_OPTION,
JOptionPane.PLAIN_MESSAGE);
panel.remove(component);
JComboBox<String> comboBox = new JComboBox<String>();
comboBox.addItem("Testing..");
comboBox.addItem("1.. 2.. 3..");
panel.add(comboBox);
// I have tried with only one of the below lines and without any also...
// Doesn't seem to have an effect.
// I've also tried invoking the below methods on the panel instead.
panel.revalidate();
panel.repaint();
JOptionPane.showConfirmDialog(null, panel, "Test",
JOptionPane.OK_OPTION,
JOptionPane.PLAIN_MESSAGE);
}
}
I.e. the label could be second in a series of three, I would want the combo box to remain second when the component is changed to the combo box. Hence why I was trying to change the reference
Use a Card Layout. It will replace a component at the same location.
Suppose I have a Java application that has more than one component in which you can enter text. Now suppose this application also has a dialog that lets you insert a single character (like the dialog in Word that comes up when you select Insert from the Edit menu) into those components. You want it to insert the character into whichever text component last had the focus.
But how do you know which text component last had the focus?
I could keep track of this manually, by having each text component report to the application whenever it gets the focus and then have the application insert the new character into whichever component that last had the focus.
But this must be a common problem (consider Paste buttons in tool bars---how does it know where to paste it into?). Is there something already built in to Swing that lets you get a handle to the last text component that had the focus? Or do I need to write this myself?
Is there something already built in to Swing that lets you get a handle to the last text component that had the focus?
You create an Action that extends TextAction. The TextAction class has a method that allows you to obtain the last text component that had focus.
Edit:
You can create your own Action and do whatever you want. The Action can then be added to any JMenuItem or JButton. For example:
class SelectAll extends TextAction
{
public SelectAll()
{
super("Select All");
}
public void actionPerformed(ActionEvent e)
{
JTextComponent component = getFocusedComponent();
component.selectAll();
}
}
If you just want to insert a character at the caret position of the text field then you can probably just do
component.replaceSelection(...);
Edit 2:
I don't understand what the confusion is with this answer. Here is a simple example:
select some text
use the mouse to click on the check box
tab or use the mouse to click on the "Cut" button
It doesn't matter that the text field doesn't currently have focus when the Action is invoked. The TextAction tracks the last text component that had focus.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
public class TextActionTest extends JFrame
{
JTextField textField = new JTextField("Select Me");
JTabbedPane tabbedPane;
public TextActionTest()
{
add(textField, BorderLayout.NORTH);
add(new JCheckBox("Click Me!"));
add(new JButton(new CutAction()), BorderLayout.SOUTH);
}
public static void main(String[] args)
{
TextActionTest frame = new TextActionTest();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
class CutAction extends TextAction
{
public CutAction()
{
super("Click to Cut Text");
}
public void actionPerformed(ActionEvent e)
{
JTextComponent component = getFocusedComponent();
// JTextComponent component = getTextComponent(e);
component.cut();
}
}
}
Just like suggested by #lesmana (+1 for that).
Here you have an example that shows that on focusLost the focus listener returns the previously focused component.
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Focusing
{
public static void main(String[] args)
{
JPanel p = new JPanel();
JTextField tf1 = new JTextField(6);
tf1.setName("tf1");
p.add(tf1);
JTextField tf2 = new JTextField(6);
tf2.setName("tf2");
p.add(tf2);
FocusListener fl = new FocusListener()
{
#Override
public void focusGained(FocusEvent e)
{
System.out.println("focusGained e.getSource().c=" + ((JComponent) e.getSource()).getName());
}
#Override
public void focusLost(FocusEvent e)
{
System.out.println("focusLost e.getSource().c=" + ((JComponent) e.getSource()).getName());
}
};
tf1.addFocusListener(fl);
tf2.addFocusListener(fl);
JPanel contentPane = new JPanel();
contentPane.add(p);
JFrame f = new JFrame();
f.setContentPane(contentPane);
f.setSize(800, 600);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
}
All the best, Boro.
I've never done this directly, but you could look into the FocusEvents and the Focus Subsystem.
Hopefully there is something in the Focus Subsystem that would fire events that you could listen for.
You can register a FocusListener to every text component. The FocusEvent object has a reference to the last component which had focus.